在即将推出的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 } });
可悲的是,TS编译器将此类型视为“过深或无限大”。 有什么方法可以以不引发错误的方式重新定义它?
答案 0 :(得分:1)
在TS 4.0中,您可以使用Variadic Tuple Types获得与Array
类型完全相同的元素类型。将其与Recursive Type Aliases结合使用,即可实现所需的功能。前往TS游乐场
进一步的解释:
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
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
答案 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[]
)。