Ree*_*mes 4 types typescript typescript-generics typescript-typings
我将如何通过在提供的元组中通过任意类型过滤提供的元组类型来生成新的元组类型?
示例(游乐场):
type Journey = ["don't", 'stop', 'believing'];
type ExcludeFromTuple<T extends unknown[], E> = ????;
type DepressingJourney = ExcludeFromTuple<Journey, "don't">; // type should be ['stop', 'believing']
Run Code Online (Sandbox Code Playgroud)
请注意,该解决方案不需要事先确保类型E存在于类型T中,如果存在,只需将其删除。
尽管这里的示例很简单,但我有一个更复杂的用例,我希望能够通过我正在编写的库的使用者定义的任意类型过滤掉。
尽管 TypeScript 本身支持exclude 类型,但它仅适用于联合类型,而且我一直无法找到元组的等效项。
类似的类型ExcludeFromTuple对于生成其他实用程序类型非常有用。
type RemoveStringsFromTuple<T extends unknown[]> = ExcludeFromTuple<T, string>;
type RemoveNumbersFromTuple<T extends unknown[]> = ExcludeFromTuple<T, number>;
type RemoveNeversFromTuple<T extends unknown[]> = ExcludeFromTuple<T, never>;
type RemoveUndefinedsFromTuple<T extends unknown[]> = ExcludeFromTuple<T, undefined>;
Run Code Online (Sandbox Code Playgroud)
我有一种感觉,该类型需要利用 TypeScript 2.8 的条件类型、TypeScript 3.1 的元组映射类型以及某种类型的递归类型魔法的组合,但我一直无法弄清楚也找不到任何人谁有。
TS 4.1+ 更新:
使用TS 4.0 中引入的可变参数元组类型和TS4.1 中引入的递归条件类型,您现在可以ExcludeFromTuple更简单地编写如下:
type ExcludeFromTuple<T extends readonly any[], E> =
T extends [infer F, ...infer R] ? F extends E ? ExcludeFromTuple<R, E> :
[F, ...ExcludeFromTuple<R, E>] : []
Run Code Online (Sandbox Code Playgroud)
您可以验证这是否按需要工作:
type DepressingJourney = ExcludeFromTuple<Journey, "don't">;
// type should be ['stop', 'believing']
type SlicedPi = ExcludeFromTuple<[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9], 1 | 9>
// type SlicedPi = [3, 4, 5, 2, 6, 5, 3, 5, 8, 7]
Run Code Online (Sandbox Code Playgroud)
TS-4.1 之前的答案:
哎呀,这确实需要递归条件类型,而 TypeScript 尚不支持这些类型。如果您想使用它们,您需要自担风险。通常我宁愿写一个应该是递归的类型,然后将它展开到一个固定的深度。所以,而不是type F<X> = ...F<X>...,我写type F<X> = ...F0<X>...; type F0<X> = ...F1<X>...;。
为了写这个,我想对元组使用基本的“列表处理”类型,即在元组上添加一个Cons<H, T>类型;获取元组的第一个元素,并获取删除第一个元素的元组。你可以这样定义:HTHead<T>TTail<T>T
type Cons<H, T> = T extends readonly any[] ? ((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never : never;
type Tail<T extends readonly any[]> = ((...t: T) => void) extends ((h: any, ...r: infer R) => void) ? R : never;
type Head<T extends readonly any[]> = T[0];
Run Code Online (Sandbox Code Playgroud)
然后递归类型看起来像这样:
/* type ExcludeFromTupleRecursive<T extends readonly any[], E> =
T["length"] extends 0 ? [] :
ExcludeFromTupleRecursive<Tail<T>, E> extends infer X ?
Head<T> extends E ? X : Cons<Head<T>, X> : never; */
Run Code Online (Sandbox Code Playgroud)
这个想法是:取元组的尾部T并对其执行ExcludeFromTupleRecursive。这就是递归。然后,对于结果,当且仅当它不匹配时,您才应该添加元组的头部E。
但这是非法循环,所以我像这样展开它:
type ExcludeFromTuple<T extends readonly any[], E> = T["length"] extends 0 ? [] : X0<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X0<T extends readonly any[], E> = T["length"] extends 0 ? [] : X1<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X1<T extends readonly any[], E> = T["length"] extends 0 ? [] : X2<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X2<T extends readonly any[], E> = T["length"] extends 0 ? [] : X3<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X3<T extends readonly any[], E> = T["length"] extends 0 ? [] : X4<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X4<T extends readonly any[], E> = T["length"] extends 0 ? [] : X5<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X5<T extends readonly any[], E> = T["length"] extends 0 ? [] : X6<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X6<T extends readonly any[], E> = T["length"] extends 0 ? [] : X7<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X7<T extends readonly any[], E> = T["length"] extends 0 ? [] : X8<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X8<T extends readonly any[], E> = T["length"] extends 0 ? [] : X9<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type X9<T extends readonly any[], E> = T["length"] extends 0 ? [] : XA<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type XA<T extends readonly any[], E> = T["length"] extends 0 ? [] : XB<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type XB<T extends readonly any[], E> = T["length"] extends 0 ? [] : XC<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type XC<T extends readonly any[], E> = T["length"] extends 0 ? [] : XD<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type XD<T extends readonly any[], E> = T["length"] extends 0 ? [] : XE<Tail<T>, E> extends infer X ? Head<T> extends E ? X : Cons<Head<T>, X> : never;
type XE<T extends readonly any[], E> = T; // bail out
Run Code Online (Sandbox Code Playgroud)
还玩得开心吗?让我们看看它是否有效:
type DepressingJourney = ExcludeFromTuple<Journey, "don't">;
// type should be ['stop', 'believing']
type SlicedPi = ExcludeFromTuple<[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9], 1 | 9>
// type SlicedPi = [3, 4, 5, 2, 6, 5, 3, 5, 8, 7]
Run Code Online (Sandbox Code Playgroud)
在我看来很好。
| 归档时间: |
|
| 查看次数: |
634 次 |
| 最近记录: |