TypeScript 按任意类型过滤元组类型 [英] TypeScript filter tuple type by an arbitrary type

查看:26
本文介绍了TypeScript 按任意类型过滤元组类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

How would I go about generating a new tuple type by filtering a provided tuple type by an arbitrary type within the provided tuple?

实施例(游乐场):

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

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

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

请注意,该解决方案不需要事先确保类型 E 存在于类型 T 中,如果存在,只需将其删除.

Note that the solution doesn't need to ensure that type E exists in type T before hand, it just need to remove it if it does.

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

Although the example is simple here, I have a more complicated use case where I want to be able to filter out by an arbitrary type defined by a consumer of the library I am writing.

虽然 TypeScript 本身支持 排除类型,但它仅适用于联合类型,我一直无法找到元组的等效项.

Although TypeScript natively supports the exclude type, it only works on union types, and I have been unable to find an equivalent for tuples.

ExcludeFromTuple 这样的类型对于生成其他实用程序类型非常有用.

A type like ExcludeFromTuple would be extremely useful for generating other utility types.

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.8 的 条件类型,TypeScript 3.1 的 元组上的映射类型,以及某种类型的递归类型魔术,但我一直无法弄清楚也找不到任何人.

I have a feeling the type would need to leverage a combination of TypeScript 2.8’s conditional types, TypeScript 3.1’s mapped types on tuples, and some type of recursive type magic, but I haven't been able figure it out nor find anyone who has.

推荐答案

针对 TS 4.1+ 的更新:

Update for TS 4.1+:

使用 可变元组TS 4.0 中引入的类型,以及 递归条件类型在 TS4.1 中引入,您现在可以更简单地编写 ExcludeFromTuple 为:

With variadic tuple types introduced in TS 4.0, and recursive conditional types introduced in TS4.1, you can now write ExcludeFromTuple more simply as:

type ExcludeFromTuple<T extends readonly any[], E> =
    T extends [infer F, ...infer R] ? F extends E ? ExcludeFromTuple<R, E> :
    [F, ...ExcludeFromTuple<R, E>] : []

您可以验证这是否按预期工作:

You can verify that this works as desired:

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 之前的答案:

Pre TS-4.1 answer:

糟糕,这真的需要递归条件类型,但不支持在 TypeScript 中.如果您想使用它们,请风险自担.通常我宁愿写一个应该递归的类型,然后将它展开到一个固定的深度.所以代替 输入 F= ...F...,我写type F=...F0 X ...;F0 X 型= ...F1...;.

Yuck, this is something that really needs recursive conditional types which are not supported in TypeScript yet. If you want to use them you do so at your own risk. Usually I'd rather write a type that should be recursive and then unroll it into a fixed depth. So instead of type F<X> = ...F<X>..., I write type F<X> = ...F0<X>...; type F0<X> = ...F1<X>...;.

要写这个,我想使用基本的列表处理";元组的类型,即 Cons 将类型 H 附加到元组 T 上;Head 获取元组的第一个元素 TTail 获取元组 Tcode> 删除第一个元素.你可以这样定义:

To write this I'd want to use basic "list processing" types for tuples, namely Cons<H, T> to prepend a type H onto a tuple T; Head<T> to get the first element of a tuple T, and Tail<T> to get the tuple T with the first element removed. You can define those like this:

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];

然后递归类型看起来像这样:

Then the recursive type would look something like this:

/* 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 不匹配时,您才应该添加元组的头部.

The idea is: take the tail of the tuple T and perform ExcludeFromTupleRecursive on it. That's the recursion. Then, to the result, you should prepend the head of the tuple if and only if it doesn't match E.

但这是非法循环,所以我将它展开如下:

But that's illegally circular, so I unroll it like this:

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

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

Having fun yet? Let's see if it works:

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]

我觉得不错.

链接到代码

这篇关于TypeScript 按任意类型过滤元组类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆