首页 > 解决方案 > TS4.1:有没有办法定义这种不太深的属性路径类型?

问题描述

尝试使用即将推出的 TypeScript 4.1 的模板文字类型,我尝试定义一个可以检查属性路径的泛型类型。

在 TS 4.1 之前,无法键入诸如 之类的表达式'foo.bar.baz',因此您必须满足于string. 现在,使用模板文字类型,我希望能够键入这些属性路径,并将它们用于诸如 MongoDB 查询和投影对象之类的事情。例如:

db.someCollection.find({ 'foo.bar.baz': { $exists: true } });

这是我想出的类型:

type PropsPath<T> =
    T extends object
        ? T extends any[]
            ? number
            : {
                [P in keyof T]: P | `${P}.${PropsPath<T[P]>}`
            }[keyof T]
        : '';

TS Playground 中的完整示例

遗憾的是,TS 编译器认为这种类型“过深或可能无限”。有没有办法以不引发错误的方式重新定义它?

标签: typescripttypescript-generics

解决方案


使用 TS 4.0,您可以使用Variadic Tuple Types来获得类型的确切元素Array类型。将它与Recursive Type Aliases结合起来,您可以实现您想要的。看看 TS 游乐场

TS游乐场

进一步说明:

  • ExtractPropsPath<T>T: 这个别名只保留它的任何成员string[]。例如:['arr'] | ['arr', ...any[]],这个别名只会保留['arr']
  • Join[...Elements][]:用分隔符加入 an D。在这种情况下,分隔符是一个点.
  • PathOf: 解决方案的关键就在这里。PathOf返回一个联合类型,它将:
    • [key]如果T[key]是原语则返回
    • 返回[key] | [key, ...nested-if]ifT[key]是一个对象(注意...,我们将返回数组类型)
    • “nested-if”是我们检查是否
      • T[key]是一个有 1 个成员的元组(例如:),如果成员类型是原始的,[number]则返回['0'],或者我们返回递归PathOf<member>(将此点标记为 {1})
      • T[key]是具有多个成员的元组或数组类型(例如:[number, string]number[])然后
        • 检查元组是否是联合(例如:[number, string]-> number | string)然后只返回递归PathOf
        • 如果不是联合,那么它是一个数组类型,我们可以使用与 {1} 相同的逻辑
  • SerializedPathOf: 基本上将PathOf它的并集转换为带有分隔符string[]的联合并集(例如:-> )string.['obj'] | ['obj', 'num']'obj' | 'obj.num'

推荐阅读