TypeScript filter tuple type by an arbitrary type
如何通过在提供的元组中用任意类型过滤提供的元组类型来生成新的元组类型?
示例(游乐场):
1 2 3 4 5 | type Journey = ["don't", 'stop', 'believing']; type ExcludeFromTuple<T extends unknown[], E> = ????; type DepressingJourney = ExcludeFromTuple<Journey,"don't">; // type should be ['stop', 'believing'] |
请注意,该解决方案无需先确保类型
尽管这里的示例很简单,但我有一个更复杂的用例,其中我希望能够按我正在编写的库的使用者定义的任意类型过滤掉。
尽管TypeScript本身支持排除类型,但它仅适用于联合类型,并且我无法找到等效的元组。
类似
1 2 3 4 | 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>; |
我觉得该类型需要利用TypeScript 2.8a的条件类型的组合,
TypeScript 3.1a在元组上的映射类型,以及某种类型的递归类型魔术,但是我一直无法弄清它,也找不到任何有此能力的人。
对于TS 4.1的更新:
有了TS 4.0中引入的可变参数元组类型和TS4.1中引入的递归条件类型,您现在可以更简单地将
1 2 3 | type ExcludeFromTuple<T extends readonly any[], E> = T extends [infer F, ...infer R] ? F extends E ? ExcludeFromTuple<R, E> : [F, ...ExcludeFromTuple<R, E>] : [] |
您可以验证它是否可以按需工作:
1 2 3 4 5 | 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] |
游乐场链接到代码
TS-4.1之前版本的答案:
好吧,这确实需要TypeScript尚不支持的递归条件类型。如果要使用它们,后果自负。通常,我宁愿编写一个应该递归的类型,然后将其展开到固定的深度。因此,我写的是
要编写此代码,我想对元组使用基本的"列表处理"类型,即
1 2 3 | 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]; |
然后递归类型将如下所示:
1 2 3 4 | /* 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; */ |
这个想法是:取元组
但这是非法通函,因此我将其展开为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
玩得开心吗?让我们看看它是否有效:
1 2 3 4 5 | 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] |
对我很好。
链接到代码