在Typescript中推断深层嵌套对象的类型

时间:2019-10-18 13:38:14

标签: typescript lookup type-inference

我正在尝试创建一个函数,该函数可以安全地在深度嵌套的对象(在这种情况下为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 个答案:

答案 0 :(得分:1)

我倾向于避免使用所有条件类型,因为编译器无法很好地推论它们,并且看起来您不需要它们。除了type Foo<T> = T extends U ? F<T> : never之外,您还可以像T那样constrain type Foo<T extends U> = Foo<T>,对于编译器来说更简单。

这里的解决方案可能是使getStyle() generic具有足够的类型参数,以使编译器理解每个参数都在钻取到对象及其属性looking up中。像这样:

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的类型为Themename的类型为一般类型K约束为keyof Theme,{{1} }具有约束到style的某种通用类型S,而keyof Theme[K]具有约束到mod的某种通用类型M。这使得keyof Theme[K][S]可以无错误地进行编译,并且将t[name][style][mod]的返回类型推断为getStyle(),这意味着输出也将被相当强地键入:

Theme[K][S][M]

好的,希望能有所帮助。祝你好运!

Link to code