TypeScript筛选元组类型的任意类型

时间:2019-11-21 21:37:59

标签: typescript types typescript-typings typescript-generics

如何通过在提供的元组中用任意类型过滤提供的元组类型来生成新的元组类型?

示例(Playground):

type Journey = ["don't", 'stop', 'believing'];

type ExcludeFromTuple<T extends unknown[], E> = ????;

type DepressingJourney = ExcludeFromTuple<Journey, "don't">; // type should be ['stop', 'believing']

请注意,该解决方案无需事先确保类型{{1}中存在类型E,只需删除它即可。

尽管这里的示例很简单,但我有一个更复杂的用例,其中我希望能够按我正在编写的库的使用者定义的任意类型进行过滤。

尽管TypeScript本机支持exclude type,但它仅适用于联合类型,而且我无法找到等效的元组。

T这样的类型对于生成其他实用程序类型将非常有用。

ExcludeFromTuple

我觉得该类型需要利用TypeScript 2.8的conditional types的组合, TypeScript 3.1的mapped types on tuples和某种类型的递归类型魔术,但是我一直无法弄清它,也找不到找到的人。

提前谢谢您!

1 个答案:

答案 0 :(得分:2)

好吧,这确实需要recursive conditional types,但TypeScript尚不支持。如果要使用它们,请at your own risk。通常,我宁愿写一个应该递归的类型,然后将其展开到固定的深度。因此,我写了type F<X> = ...F<X>...而不是type F<X> = ...F0<X>...; type F0<X> = ...F1<X>...;

要编写此代码,我想对元组使用基本的“列表处理”类型,即Cons<H, T>将类型H放在元组T之前; Head<T>获取元组T的第一个元素,Tail<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];

然后递归类型将如下所示:

/* 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; */

想法是:取元组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

玩得开心吗?让我们看看它是否有效:

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]

对我很好。希望能有所帮助。祝你好运!

Link to code