类型的打字稿扩展运算符 [英] Typescript spread operator for type

查看:23
本文介绍了类型的打字稿扩展运算符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试定义一个类型,它获取一个函数类型作为泛型参数并返回一个与输入函数类型相同的函数类型,只是在最后多了一个参数:

I'm trying to define a type which gets a function type as a generic parameter and returns a function type which is the same as the input function type except it has one more argument at the end:

  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 似乎不起作用,但是如果我将其更改为这样的内容,它会起作用:

...Args seems not working, however if I change it to something like this it works:

  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 元函数定义一个这样的类型.

But it only works for a specific number of arguments and I need to define one such type for each n-ary function.

推荐答案

TypeScript 可以相当容易地将类型前置表示到元组类型上,称为 Cons 像这样:

TypeScript can fairly easily represent prepending a type onto a tuple type, called Cons<H, T> like this:

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 以将类型追加到元组的末尾:

You can use this definition along with conditional mapped tuple types to produce a Push<T, V> to append a type onto the end of a tuple:

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 元组有 可选元素,或者如果它来自函数的参数列表,您会注意到编译器将可选标记和参数名称移动"到右侧一个元素:

but this definition of Push is fragile. If the T tuple has optional elements, or if it comes from the parameter list of a function, you'll notice that the compiler "shifts" the optional markers and parameter names one element to the right:

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

参数名称实际上并不是类型的一部分,因此虽然很烦人,但它不会影响实际类型.在可选参数之后附加一个参数是......很奇怪,所以我不确定那里的正确行为是什么.不确定这些是否适合您,但请注意.

Parameter names are not really part of the type so while it's annoying it's not affecting the actual type. Appending an argument after an optional one is... strange, so I'm not sure what the right behavior there. Not sure if these are dealbreakers for you, but be warned.

无论如何你的 AugmentParam 看起来像:

Anyway your AugmentParam would look like:

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

它的工作原理(有之前的警告):

and it works (with the earlier caveats):

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

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

Okay, hope that helps. Good luck!

链接到代码

这篇关于类型的打字稿扩展运算符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆