将类型推到元组的末尾

时间:2019-10-24 17:49:09

标签: typescript tuples

我可以在元组的开头添加元素,或从其中删除元素

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;

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

即使我编写了函数

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

打字稿says会返回any[]

是否有一种方法可以将新类型推入元组的末尾,如(不支持)

export type PushTuple<T extends any[], X> = [...T, X];

1 个答案:

答案 0 :(得分:3)

我通常根据您所说的Push(但我称之为Cons)来定义Unshift,如下所示:

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;

Push<T, V>的工作方式是使元组比T长一个元素(它与Cons一样),然后再使其maps over it。它用来自T的相应元素填充输出的初始元素,然后将剩下的所有内容填充到V中。长度为 n + 1 的映射元组中唯一不是长度为 n 的元组的索引的元素索引是索引 n 本身,这意味着新元组的最后一个元素是V。 (如果是联合身份,我确保在Tdistribute。)

请注意,这仅适用于非边缘情况……不要期望readonlyoptionalrest元组元素或非元组数组表现良好;如果要定义这些限制,则必须自己解决这些限制。


因此,您可以这样定义f()

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

(请注意,我使用overload来使自己免于担心编译器尝试和不了解实现是否符合Push<Z, X>的问题:

它可以按您期望的那样工作:

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

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

Link to code