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

Inferring types of deeply nested object in Typescript

我正在尝试创建一个函数来安全地查找深层嵌套对象中的 属性(在本例中为 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; }; }'.

看起来无法查找具有联合类型的联合类型,中途需要某种推理。

有什么想法吗?

我倾向于避免所有条件类型,因为编译器不能很好地推理它们,而且看起来您不需要它们。除了 type Foo<T> = T extends U ? F<T> : never,您还可以 constrain T,例如 type Foo<T extends U> = Foo<T>,这对编译器来说更直接。

这里的解决办法大概是让getStyle()generic in enough type parameters that the compiler understands that each parameter is drilling down into an object and 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];

这里我们说 tTheme 类型,name 是某种限制为 keyof Theme 的泛型类型 Kstyle 是一些泛型类型 S 限制为 keyof Theme[K],而 mod 是一些泛型类型 M 限制为 keyof Theme[K][S]。这允许 t[name][style][mod] 编译没有错误,并且 getStyle() 的 return 类型被推断为 Theme[K][S][M],这意味着输出也将是相当强类型的:

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

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

Link to code