推断 Typescript 中深层嵌套对象的类型 [英] Inferring types of deeply nested object in Typescript

查看:43
本文介绍了推断 Typescript 中深层嵌套对象的类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个函数,该函数可以安全地查找深度嵌套对象(在本例中为 theme)中的属性.我应该提到我不想静态定义它的类型,因为它们旨在成为频繁更改的主题,因此使用 typeof

I'm trying to create a function that safely looks up the property in deeply nested object (theme in this case). I should mention that I don't want to statically define its type because they are intended to be a subject to frequent change and thus use typeof

此函数接受对象本身和 3 个键,但问题是 — 我无法正确推断所有这些键的类型.

This functions accepts the object itself and 3 keys, but the problem is — I can't correctly infer types for all of them.

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 中的结果:

The result of this in 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; }; }'.

看起来不可能使用联合类型和某种需要中途的推理来查找联合类型.

Looks like it is not possible to look up union types with union types and some kind of inference required midway.

有什么想法吗?

推荐答案

我倾向于避免所有条件类型,因为编译器不能很好地推理它们,而且看起来您不需要它们.而不是 类型 Foo= T 扩展 U ?F T:永远,你可以constrain T,如type Foo= Foo,这对编译器来说更直接.

I'd be inclined to avoid all conditional types, since the compiler can't reason about them very well and it doesn't look like you need them. Instead of type Foo<T> = T extends U ? F<T> : never, you could just constrain T, like type Foo<T extends U> = Foo<T>, which is more straightforward for the compiler.

这里的解决方案可能是使 getStyle() 泛型 在足够多的类型参数中,编译器可以理解每个参数都在深入到一个对象和 查找 其属性.像这样:

The solution here is probably to make getStyle() generic in enough type parameters that the compiler understands that each parameter is drilling down into an object and looking up its properties. Like this:

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是某种K约束的泛型类型到 keyof Themestyle 是某种通用类型 S 约束到 keyof Theme[K],并且mod 是某种通用类型 M 约束到 keyof Theme[K][S].这允许 t[name][style][mod] 编译没有错误,并且 getStyle() 的返回类型被推断为 Theme[K][S][M],这意味着输出也将是相当强类型的:

Here we say that t is of type Theme, that name is of some generic type K constrained to keyof Theme, that style is of some generic type S constrained to keyof Theme[K], and that mod is of some generic type M constrained to keyof Theme[K][S]. That allows t[name][style][mod] to compile with no error, and the return type of getStyle() is inferred to be Theme[K][S][M], meaning the output will be fairly strongly typed also:

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

好的,希望有帮助.祝你好运!

Okay, hope that helps. Good luck!

代码链接

这篇关于推断 Typescript 中深层嵌套对象的类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆