TS4.1:有没有一种方法可以定义此属性路径类型,而该类型不太深?

时间:2020-09-24 12:10:30

标签: typescript typescript-generics

在即将推出的TypeScript 4.1的template literal types上进行了实验,我试图定义一个可以检查属性路径的通用类型。

直到TS 4.1,尚无法键入if b.startswith('y'): print('you fell and died') 之类的表达式,因此您必须满足'foo.bar.baz'的要求。现在,对于模板文字类型,我希望能够键入这些属性路径,并将其用于诸如MongoDB查询和投影对象之类的事情。例如:

string

这是我想出的类型:

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

Full example in TS Playground

可悲的是,TS编译器将此类型视为“过深或无限大”。 有什么方法可以以不引发错误的方式重新定义它?

3 个答案:

答案 0 :(得分:1)

在TS 4.0中,您可以使用Variadic Tuple Types获得与Array类型完全相同的元素类型。将其与Recursive Type Aliases结合使用,即可实现所需的功能。前往TS游乐场

TS Playground

进一步的解释:

  • ExtractPropsPath<T>:此别名仅保留T的任何成员,即string[]。例如:['arr'] | ['arr', ...any[]],此别名将仅保留['arr']
  • Join:将[...Elements][]与定界符D连接起来。在这种情况下,定界符是点.
  • PathOf:解决方案的重点在这里。 PathOf返回的联盟类型将:
    • 如果[key]是原始元素,则返回T[key]
    • 如果[key] | [key, ...nested-if]是一个对象,则返回T[key](注意...,我们将返回数组类型)
    • “嵌套-如果”是我们检查是否
      • T[key]是具有1个成员(例如:[number])的元组,如果成员类型为基本类型,则返回['0'],否则我们返回递归{{1 }}(将此点标记为{1})
      • PathOf<member>是具有多个成员或数组类型(例如:T[key][number, string])的元组
        • 检查元组是否为联合(例如:number[]-> [number, string]),然后仅返回递归number | string
        • 如果不是联合,则为数组类型,我们可以使用与{1}相同的逻辑
  • PathOf:基本上是将SerializedPathOf的联合体PathOf转换为string[]与分隔符string的联合联合体(例如:{{1 }}-> .

答案 1 :(得分:1)

基于Amit的答案,它也适用于数组,尽管它不考虑Chau的元组。

type Path<T> = T extends Array<any>
  ? `${number}` | `${number}.${Path<T[number]>}`
  : T extends object
  ? {
      [P in keyof T]: (P & string) | `${P & string}.${Path<T[P]>}`
    }[keyof T]
  : never

TS Playground

答案 2 :(得分:0)

要回答我自己的问题,我终于想出了以下类型:

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

Updated example in TS Playground

它可以工作,但可悲的是没有涵盖T具有嵌套属性的情况,该属性是长度未知的数组(any[])。