Typescript 将类型推送到元组的末尾,可选择跳过

Typescript 将类型推送到元组的末尾,可选择跳过,typescript,tuples,Typescript,Tuples,我知道如何将类型推送到元组的末尾: type Cons<H, T extends readonly any[]> = ((head: H, ...tail: T) => void) extends ((...cons: infer R) => void) ? R : never; type Push<T extends readonly any[], V> = T extends any ? Cons<void, T> exten

我知道如何将类型推送到元组的末尾:

type Cons<H, T extends readonly any[]> =
    ((head: H, ...tail: T) => void) extends ((...cons: infer R) => void) ? R : never;

type Push<T extends readonly any[], V>
    = T extends any ? Cons<void, T> extends infer U ?
    { [K in keyof U]: K extends keyof T ? T[K] : V } : never : never;


事实上,我正试图做到:

函数doSmth number>(f:f,…args:Push){
var x:string=args.pop();
返回f(…args)+x;
}
函数f1(x:编号,y:编号){
返回x+y;
}
函数f2(x:编号,y:编号){
返回x+(y | | 0);
}
doSmth(f1,1,2,“A”);
doSmth(f2,1,2,“B”);
doSmth(f2,1,“C”);//这里的问题是:应该编译,但它没有
doSmth(f2,1,2);//这里:不应该编译,因为last不是字符串,但它确实是
doSmth(f2,1,未定义的“D”);//可能,但实际上我不在乎

更新:TypeScript 4.0将提供更灵活的内置元组操作功能<代码>推送将简单地实现为
[…T,V]


TS4.0之前的答案:

啊,为什么?!嗯,我的意思是,我也许可以这样做,但是越是玩杂耍,我就越不推荐做任何重要的事情。有一个名为TypeScript的库(尽管它至少在中不起作用,所以我不打算给出需要它的堆栈溢出答案),在这里您可能可以构建一些有效的东西

我的方法是将具有可选元素的元组转换为没有可选元素的元组的并集。不幸的是,我缺少一个内置的方法来获取一个像
6
这样的数字类型,并获得一个这样长度的元组。所以我正在制作一个硬编码的元组列表,这些元组的长度不同,我可以映射。如果需要对更长的元组进行处理,可以对其进行扩展。我们开始:

type Cons<H, T extends readonly any[]> =
    ((head: H, ...tail: T) => void) extends ((...cons: infer R) => void) ? R : never;
这是一个巨大的元组列表。因此,Tup[4]就是
[0,0,0,0]
,等等

type TruncateTuple<T extends readonly any[], N extends number> = Extract<
    Tup[N] extends infer R ? { [K in keyof R]: K extends keyof T ? T[K] : never }
    : never, readonly any[]>;
这是主要事件
OptTupleToUnion
获取一个元组
T
,并从中生成一个非可选元组的并集。它通过将
Required
(即
T
,将可选元素转换为Required元素)截断为length
T['length']
,这是
T
的可能长度的并集。因此,
OptTupleToUnion
应该变成
[1,2]|[1,2,3]|[1,2,3,4]

type Tup = [[], [0], [0, 0], [0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]; // make as long as you need
然后我将旧的
Push
重命名为
\u Push

type _Push<T extends readonly any[], V>
    = T extends any ? Cons<void, T> extends infer U ?
    { [K in keyof U]: K extends keyof T ? T[K] : V } : never : never;
(使用相同的
T扩展any?.T..:永不
,以确保联合会得到分发)

让我们看看它是否有效:

type A = Push<[1, 2, 3], 4>;  // [1, 2, 3, 4]
type B = Push<[1, 2, 3?], 4>; // [1, 2, 3 | undefined, 4?]
type A = Push<[1, 2, 3], 4>;  // [1, 2, 3, 4]
type B = Push<[1, 2, 3?], 4>; // [1, 2, 3, 4] | [1, 2, 4]
type A=Push;//[1, 2, 3, 4]
类型B=推送;//[1, 2, 3, 4] | [1, 2, 4]
耶,看起来不错。
type OptTupleToUnion<T extends readonly any[]> =
    TruncateTuple<Required<T>, T['length']>;
type _Push<T extends readonly any[], V>
    = T extends any ? Cons<void, T> extends infer U ?
    { [K in keyof U]: K extends keyof T ? T[K] : V } : never : never;
type Push<T extends readonly any[], V> = T extends any ?
    _Push<OptTupleToUnion<T>, V> : never;
type A = Push<[1, 2, 3], 4>;  // [1, 2, 3, 4]
type B = Push<[1, 2, 3?], 4>; // [1, 2, 3, 4] | [1, 2, 4]