将类型推到元组的末尾

Qwe*_*tiy 6 tuples typescript

我可以在元组的开头添加元素,或者从那里删除它

type ShiftTuple<T extends any[]> = ((...t: T) => void) extends ((x: infer X, ...r: infer R) => void) ? R : never;
type UnshiftTuple<X, T extends any[]> = ((x: X, ...t: T) => void) extends ((...r: infer R) => void) ? R : never;
Run Code Online (Sandbox Code Playgroud)

但是我很难用最后一个元素而不是第一个元素做同样的事情。

即使我写了函数

function f<X, Z extends any[]>(x: X, ...args: Z) {
  return [...args, x]
}
Run Code Online (Sandbox Code Playgroud)

打字稿说它返回any[].

有没有办法将新类型推入元组的末尾(不支持)

export type PushTuple<T extends any[], X> = [...T, X];
Run Code Online (Sandbox Code Playgroud)

jca*_*alz 5

TS4.0+ 更新

TypeScript 4.0 引入了可变元组类型,它支持Push以更直接的方式到达元组末尾......如下所示:

type Push<T extends readonly any[], V> = [...T, V];
Run Code Online (Sandbox Code Playgroud)

TS3.9 的旧答案-:

我通常Push根据你所调用的内容Unshift(但我称之为Cons)来定义,如下所示:

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;
Run Code Online (Sandbox Code Playgroud)

有效的方法Push<T, V>是使元组比它长一个元素T(它是这样做的Cons),然后映射到它上面。它用 中的相应元素填充输出的初始元素T,然后填充剩余的任何内容V长度为n+1的映射元组中唯一 不是长度为n 的元组索引的元素索引是索引n本身,因此这意味着新元组的最后一个元素是V。(我确保将其分发出去T,以防它是一个工会。)

请注意,这仅适用于非边缘情况...不要期望readonly可选其余元组元素或非元组数组表现良好;如果你想定义它,你必须自己解决这些限制。


继续常规回答:

那么你可以f()这样定义:

function f<X, Z extends any[]>(x: X, ...args: Z): Push<Z, X>;
function f(x: any, ...args: any[]): any[] {
    return [...args, x]
}
Run Code Online (Sandbox Code Playgroud)

(请注意,我使用重载来使自己不必担心编译器尝试但无法理解实现是否符合Push<Z, X>

它按您的预期工作:

const s = f(4, "hey", false);
// const s: [string, boolean, number]
console.log(s); // ["hey", false, 4]
Run Code Online (Sandbox Code Playgroud)

好的,希望有帮助;祝你好运!

链接到代码