TypeScript:捕获在高阶函数中作为参数传递的函数的变量签名

时间:2020-10-27 20:11:28

标签: node.js typescript typescript-typings

我希望高阶函数能够捕获具有不同签名的传递函数的签名参数。

我不知道这是否可行,但这是我的解决方法:

type FuncA = (a: string, b: number) => void
type FuncB = (a: string) => void

type Func = FuncA | FuncB

const a: FuncA = (a: string, b: number) => {
  console.log('FuncA')
}

const b: FuncB = (a: string) => {
  console.log('FuncB')
}

// My higher order function
const c = (func: Func) => {
  // do something here...
  return (...args: Parameters<typeof func>) => {
    func(...args) // Expected 2 arguments, but got 0 or more. ts(2556). An argument for 'a' was not provided.
  }
}

我的高阶函数c无法传递func的参数 看来TypeScript无法区分类型Func的不同可能签名。

有人知道写这种代码的模式吗?

谢谢!

2 个答案:

答案 0 :(得分:0)

如果装饰功能可以是任何功能,您都可以:

const c = <T extends (...a: any) => any>(func: T) => {
  // do something here...
  return (...args: Parameters<typeof func>): ReturnType<T> => {
    return func(...args);
  }
}

调用它看起来像

c<typeof a>(a)('a', 2)

答案 1 :(得分:0)

这是一个艰难的过程,因为对于一个extend函数来说,另一个函数并不完全意味着您的想法。

我们希望c创建的函数要求参数与给定的函数相对应。因此,我们使用generic来描述函数。

const c = <F extends Func>(func: F) => {
  return (...args: Parameters<F>) => {
    func(...args); // still has error
  }
}

在这一点上,我们仍然存在该错误,但是当我们调用c时,我们会得到一个函数,该函数具有基于给定ab的正确参数。 / p>

const cA = c(a); // type: (a: string, b: number) => void
cA("", 0);

const cB = c(b); // type: (a: string) => void
cB("");

对于错误,它与一个函数扩展另一个函数的含义有关。尝试将F extends Func更改为F extends FuncAF extends FuncB以查看会发生什么。使用F extends FuncB时,我们会在c(a)上看到错误,但是使用F extends FuncA时,我们不会在c(b)上看到错误。嗯?

如果您从回调的角度考虑它,那是有道理的。传递一个需要比预期少的参数的函数是可以的,但传递一个需要更多参数的函数不是可以的。但是我们是实现回调的人,因此这给我们带来了问题。如果我们使用不带参数的函数扩展type Func,则Parameters<F>中的空数组不足以调用这两种类型。

我们必须使泛型依赖于参数。

type AP = Parameters<FuncA> // type: [a: string, b: number]
type BP = Parameters<FuncB> // type: [a: string]

type Args = AP | BP;

const c = <A extends Args>(func: (...args: A) => void) => {
  return (...args: A) => {
    func(...args) // no error
  }
}

Typescript Playground Link