Определение типов глубоко вложенных объектов в Typescript - PullRequest
0 голосов
/ 18 октября 2019

Я пытаюсь создать функцию, которая безопасно просматривает свойство глубоко вложенного объекта (theme в данном случае). Я должен упомянуть, что я не хочу статически определять его тип, потому что они предназначены для частых изменений и поэтому используют typeof

. Эта функция принимает сам объект и 3 клавиши, но проблемаесть - я не могу правильно вывести типы для всех них.

const theme = {
    button: { margin: { sm: "sm" } },
    form: { padding: { lg: "lg" } }
}

type Theme = typeof theme

type CompoName = keyof Theme;

// styles of component with give key
// { margin: ..., padding: ... }
type CompoStyle<C> = C extends CompoName ? keyof Theme[C] : never;

// string 
// S = margin, padding
type CompoStyleMod<C, S> = S extends keyof CompoStyle<C>
  ? CompoStyle<C>[S]
  : never;

const getStyle = (
    t: Theme,
    name: CompoName,
    style: CompoStyle<typeof name>,
    mod: CompoStyleMod<CompoName, typeof style>
) => {
    return t[name][style][mod]
}

Результат этого в TypeScript 3.6.3:

Element implicitly has an 'any' type because expression of type '"margin" | "padding"' can't be used to index type '{ margin: { sm: string; }; } | { padding: { lg: string; }; }'.

  Property 'margin' does not exist on type '{ margin: { sm: string; }; } | { padding: { lg: string; }; }'.

Похоже, что невозможно посмотретьна полпути объединение типов с объединением типов и какой-либо вывод.

Есть идеи?

1 Ответ

1 голос
/ 18 октября 2019

Я был бы склонен избегать всех условных типов, так как компилятор не может рассуждать о них очень хорошо, и, похоже, они вам не нужны. Вместо type Foo<T> = T extends U ? F<T> : never вы могли бы просто ограничить T, например type Foo<T extends U> = Foo<T>, что более просто для компилятора.

Решение здесь, вероятно, состоит в том, чтобы сделать getStyle() универсальный в достаточном количестве параметров типа, чтобы компилятор понимал, что каждый параметр детализируется в объект и просматривает его свойства. Например:

const getStyle = <
    K extends keyof Theme,
    S extends keyof Theme[K],
    M extends keyof Theme[K][S]
>(t: Theme, name: K, style: S, mod: M) => t[name][style][mod];

Здесь мы говорим, что t имеет тип Theme, что name имеет некоторый универсальный тип K, ограниченный keyof Theme, что style имеетнекоторый универсальный тип S ограничен keyof Theme[K], а mod имеет некоторый универсальный тип M, ограниченный keyof Theme[K][S]. Это позволяет t[name][style][mod] компилироваться без ошибок, а тип возвращаемого значения getStyle() выводится как Theme[K][S][M], что означает, что вывод будет также довольно строго типизирован:

const sm = getStyle(theme, "button", "margin", "sm"); // string
const lg = getStyle(theme, "form", "padding", "lg"); // string

Хорошо, надеюсь, чтопомогает. Удачи!

Ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...