这个问题是这里问题的延伸。
我有一个对象:
type exampleType = {
propertyOne: string
propertyTwo: number,
propertyThree: {
propertyFour: string,
propertyFive: Date,
propertySix: boolean,
}
}
Run Code Online (Sandbox Code Playgroud)
string我正在寻找一种类型,可以验证点符号(如字符串)或的路径Date。在上面的示例中,这意味着该类型编译为:
propertyOne | propertyThree.propertyFour | propertyThree.PropertyFive
Run Code Online (Sandbox Code Playgroud)
使用上面先前提出的问题,可以进行以下操作:
type PathsToStringProps<T> = T extends string ? [] : {
[K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>]
}[Extract<keyof T, string>];
type Join<T extends string[], D extends string> =
T extends [] ? never :
T extends [infer F] ? F :
T extends [infer F, ...infer R] ?
F extends string ?
`${F}${D}${Join<Extract<R, string[]>, D>}` : never : string;
type Path = Join<PathsToStringProps<exampleType>, ".">
Run Code Online (Sandbox Code Playgroud)
我试图使上述解决方案变得通用,以便我可以给出Path两个通用参数:T,它代表exampleType此处, 和V,这将string|Date在我上面的示例中。
当我尝试exampleType通用时:
type Path<T> = Join<PathsToStringProps<T>, ".">
Run Code Online (Sandbox Code Playgroud)
我收到这个错误:Excessive stack depth comparing types 'PathsToStringProps<T>' and 'string[]'.ts(2321)
我可以通过指定 T 必须表示键值对象来解决这个问题:
type Path<T extends {[key: string]: any}> = Join<PathsToStringProps<T>, ".">
Run Code Online (Sandbox Code Playgroud)
继续将值的类型限制为路径指向:
type PathsToStringProps<T, V> = T extends (V) ? [] : {
[K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K], V>]
}[Extract<keyof T, string>];
type Join<T extends string[], D extends string> =
T extends [] ? never :
T extends [infer F] ? F :
T extends [infer F, ...infer R] ?
F extends string ?
`${F}${D}${Join<Extract<R, string[]>, D>}` : never : string;
type Path<T extends {[key: string]: any}, V> = Join<PathsToStringProps<T, V>, ".">
Run Code Online (Sandbox Code Playgroud)
但我收到一个错误:
如果我从 中删除通用参数 V Path,但将其保留在 中,该参数就会消失PathsToStringProps:
type Path<T extends {[key: string]: any}> = Join<PathsToStringProps<T, string|Date>, ".">
Run Code Online (Sandbox Code Playgroud)
您的方法,其中PathsToProps<T, V>生成路径为元组,然后Join<T, D>连接元组元素以形成点路径,这对编译器来说是有问题的,因为 和PathToProps<T, V>都是Join<T, D>递归条件类型,它们并不总是很好地组合,并且经常与循环发生冲突守卫或性能问题。也许你可以调整一些东西以使它正常工作,但这不是我的第一选择。
相反,由于您似乎根本不关心元组,因此您可以直接在 内部连接字符串PathsToProps<T, V>。换句话说,您不是先构建路径然后再连接它们,而是在整个过程中进行连接。
它可能看起来像这样:
type PathsToProps<T, V> = T extends V ? "" : {
[K in Extract<keyof T, string>]: Dot<K, PathsToProps<T[K], V>>
}[Extract<keyof T, string>];
type Dot<T extends string, U extends string> =
"" extends U ? T : `${T}.${U}`
Run Code Online (Sandbox Code Playgroud)
的实现PathsToProps与您的非常相似,除了我们不是显式处理空元组[]并通过 前置到元组中[K, ...PathsToProps<T[K], V>>],而是显式使用空字符串""并通过 串联它们Dot<K, PathsToProps<T[K], V>。
该Dot<T, U>类型只是连接字符串T与已经点分字符串的简写U。你在它们之间放一个点,除非U是空的(如果你有任何对象以空字符串作为键,这在技术上是错误的。你不会,是吗?我希望不会)。关键是要确保您不会以需要删除的尾随点结束(如果您总是用点连接,那么您会得到类似的路径"foo.bar.baz.")。
让我们测试一下:
type ExampleType = {
propertyOne: string
propertyTwo: number,
propertyThree: {
propertyFour: string,
propertyFive: Date,
propertySix: boolean,
}
}
type StrOrDateEx = PathsToProps<ExampleType, string | Date>
// type StrOrDateEx = "propertyOne" | "propertyThree.propertyFour" |
// "propertyThree.propertyFive"
Run Code Online (Sandbox Code Playgroud)
看起来不错!并且没有递归警告。
| 归档时间: |
|
| 查看次数: |
1504 次 |
| 最近记录: |