推断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];
这里我们说 t
是 Theme
类型,name
是某种限制为 keyof Theme
的泛型类型 K
,style
是一些泛型类型 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
好的,希望对您有所帮助。祝你好运!
我正在尝试创建一个函数来安全地查找深层嵌套对象中的 属性(在本例中为 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];
这里我们说 t
是 Theme
类型,name
是某种限制为 keyof Theme
的泛型类型 K
,style
是一些泛型类型 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
好的,希望对您有所帮助。祝你好运!