Type的TypeScript传播算子

时间:2019-11-05 14:07:48

标签: typescript typescript-typings typescript-generics

我正在尝试定义一个将函数类型作为通用参数并返回与输入函数类型相同的函数类型的类型,只是它的末尾还有一个参数:

  type AugmentParam<F extends (...args: any[]) => any, ExtraParam> = F extends (
    ...args: infer Args
  ) => infer R
    ? (
        ...args: [
          ...Args,
          ExtraParam
        ]
      ) => R
    : never

用法示例:

type F = (x: number) => boolean
type F2 = AugmentParam<F, string> // (x: number, arg2: string) => boolean

...Args似乎不起作用,但是,如果我将其更改为类似的名称,它将起作用:

  type AugmentParam<F extends (...args: any[]) => any, ExtraParam> = F extends (
    ...args: infer Args
  ) => infer R
    ? (
        ...args: [
          Args[0],
          Args[1] /* Spread doesn't work here, so it doesn't work for arbitrary number of arguments :( */,
          ExtraParam
        ]
      ) => R
    : never

但是它仅适用于特定数量的参数,我需要为每个n元函数定义一个这样的类型。

1 个答案:

答案 0 :(得分:1)

TypeScript可以相当容易地将类型 prefixing 表示为元组类型,称为Cons<H, T>,如下所示:

type Cons<H, T extends readonly any[]> =
    ((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never

type ConsTest = Cons<1, [2, 3, 4]>;
// type ConsTest = [1, 2, 3, 4]

您可以将此定义与条件映射元组类型一起使用,以产生一个Push<T, V>来将类型追加到元组的末尾:

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

type PushTest = Push<[1, 2, 3], 4>;
// type PushTest = [1, 2, 3, 4]

但是Push的定义很脆弱。如果T元组具有optional elements,或者它来自某个函数的参数列表,您会注意到编译器将可选标记和参数名称“移”到一个元素右侧: / p>

type Hmm = (...args: Push<Parameters<(optStrParam?: string) => void>, number>) => void;
// type Hmm = (h: string | undefined, optStrParam?: number) => void

参数名称实际上不是类型的一部分,因此虽然令人讨厌,但不会影响实际类型。在参数 之后添加一个可选参数很奇怪,因此我不确定那里的正确行为。不确定这些产品是否适合您,但会被警告。

无论如何,您的AugmentParam如下:

type AugmentParam<F extends (...args: any[]) => any, ExtraParam> =
    (...args: Extract<Push<Parameters<F>, ExtraParam>, readonly any[]>)
        => ReturnType<F>

并且有效(具有早期警告):

type F = (x: number) => boolean

type F2 = AugmentParam<F, string>
// type F2 = (h: number, x: string) => boolean

type F3 = AugmentParam<F2, boolean>
// type F3 = (h: number, h: string, x: boolean) => boolean

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

Link to code