打字稿:从元组类型中删除条目

Jan*_*gge 6 tuples typescript

不知道这是可能的,但我希望能够定义一个类型转换的元组,如:[number, string, undefined, number][number, string, number](即过滤掉undefined)。

我想到了这样的事情:

type FilterUndefined<T extends any[]> = {
    [i in keyof T]: T[i] extends undefined ? /* nothing? */ : T[i];
}
Run Code Online (Sandbox Code Playgroud)

可悲的是,我很确定没有办法实现这一目标。

Prz*_*ert 14

得到它了!但是它需要很多递归魔术:

type PrependTuple<A, T extends Array<any>> =
  A extends undefined ? T : 
  (((a: A, ...b: T) => void) extends (...a: infer I) => void ? I : [])

type RemoveFirstFromTuple<T extends any[]> = 
  T['length'] extends 0 ? undefined :
  (((...b: T) => void) extends (a, ...b: infer I) => void ? I : [])

type FirstFromTuple<T extends any[]> =
  T['length'] extends 0 ? undefined : T[0]

type NumberToTuple<N extends number, L extends Array<any> = []> = {
  true: L;
  false: NumberToTuple<N, PrependTuple<1, L>>;
}[L['length'] extends N ? "true" : "false"];

type Decrease<I extends number> = RemoveFirstFromTuple<NumberToTuple<I>>['length']
type H = Decrease<4>

type Iter<N extends number, Items extends any[], L extends Array<any> = []> = {
  true: L;
  false: Iter<FirstFromTuple<Items> extends undefined ? Decrease<N> : N, RemoveFirstFromTuple<Items>, PrependTuple<FirstFromTuple<Items>, L>>;
}[L["length"] extends N ? "true" : "false"];

type FilterUndefined<T extends any[]> = Iter<T['length'], T>
type I = [number, string, undefined, number];
type R = FilterUndefined<I>


Run Code Online (Sandbox Code Playgroud)

操场

怎么运行的:

PrependToTuple是一种util,它接受项A和列表T,并在A未定义时将其添加到第一位。PrependToTuple<undefined, []> => []PrependToTuple<undefined, [number]> => [number]

RemoveFirstFromTuple 我以同样的方式工作漂亮的马赫

NumberToTuple递归检查最终元组的长度是否为N,否则,他将1加到递归调用中。创建Decrease实用程序需要此实用程序。

最重要的z Iter就像递归循环一样,当最后一个元组的长度是它的return的N大小,但是当我们尝试执行add时,它的长度不会增加,因此当我们必须减小N时。InputOutputPrependToTupleundefinedIter<FirstFromTuple<Items> extends undefined

  • 不,这不是秘密。我从 2018 年在维罗纳的反应日获得的递归元组技巧。来自@MattiaManzati 的灯光演讲。我在一些非同寻常的 TS 问题中使用了这个技巧。像 Iter 这样的类型受到它的启发 (2认同)

for*_*d04 14

TS 4.1

元组的过滤操作现在正式成为可能:

type FilterUndefined<T extends unknown[]> = T extends [] ? [] :
    T extends [infer H, ...infer R] ?
    H extends undefined ? FilterUndefined<R> : [H, ...FilterUndefined<R>] : T
Run Code Online (Sandbox Code Playgroud) 让我们做一些测试来检查它是否按预期工作:
type T1 = FilterUndefined<[number, string, undefined, number]> 
// [number, string, number]
type T2 = FilterUndefined<[1, undefined, 2]> // [1, 2]
type T3 = FilterUndefined<[undefined, 2]> // [2]
type T4 = FilterUndefined<[2, undefined]> // [2]
type T5 = FilterUndefined<[undefined, undefined, 2]> // [2]
type T6 = FilterUndefined<[undefined]> // []
type T7 = FilterUndefined<[]> // []
Run Code Online (Sandbox Code Playgroud)

更多信息


操场


Ole*_*ter 8

补充回答:

ford04 的答案中方法的扩展允许我们创建一个“拼接器”实用程序类型,它可以删除任意索引处的值(以防有人在寻找类型安全的解决方案时发现它splice)。

undefined这涉及创建一个实用程序类型,该实用程序类型将从给定的元组和索引生成元组:

type UndefIndex<T extends any[], I extends number> = {
    [ P in keyof T ] : P extends Exclude<keyof T, keyof any[]> ? P extends `${I}` ? undefined : T[P] : T[P]
}
Run Code Online (Sandbox Code Playgroud)

UndefIndex那么这只是组合和类型的问题FilterUndefined

type FilterUndefined<T extends any[]> = T extends [] ? [] :
    T extends [infer H, ...infer R] ?
    H extends undefined ? FilterUndefined<R> : [H, ...FilterUndefined<R>] : T;

type SpliceTuple<T extends any[], I extends number> = FilterUndefined<UndefIndex<T, I>>;

type a = SpliceTuple<[1,2,3], 0>; //[2,3]
type b = SpliceTuple<[1,2,3], 1>; //[1,3]
type c = SpliceTuple<[1,2,3], 2>; //[1,2]
type d = SpliceTuple<[1,2,3], 3>; //[1,2,3]
Run Code Online (Sandbox Code Playgroud)

操场