打字稿-在保留签名的同时包装功能

时间:2019-12-06 11:55:38

标签: typescript type-inference typescript-generics

我试图弄清楚如何包装已定义的函数,以便在保留其签名的同时做更多工作。这是理想的效果:

程序员定义接口:

const actions = {
    first: (id: number) => {/*...*/},
    second: (name: string) => {/*...*/}
}
let actionsInterface = wrap(actions)
export actionsInterface

actionsInterface 应该(即目标)具有以下界面:

{
    first: (id: number) => void,
    second: (name: string) => void
}

它基本上提供了与最初定义的完全相同的接口(即,具有相同功能的列表,具有相同的参数,不计算返回类型),但是正在做的其他工作由{{ 1}}。

我当前的实现类似于:

wrap()

但是,我在type VarFn = (...args: any) => any function wrap<T, K extends keyof T> (funcList: Record<K, T[K] extends VarFn ? T[K] : never>) { // this maps a specific function to a function that does something extra function wrapOne<T extends (...args: any)=>any>(fn: T) { return (...args: Parameters<typeof fn>) => { someMagicThingyExtra(fn(args)) } } // we iterate through the list and map each function to the one that's doing something extra type FuncMap = Record<K, (...args: Parameters<T[K] extends VarFn ? T[K] : never>)=>void> let map: FuncMap for (var Key in funcList) { let func = funcList[Key] map[Key] = wrapOne(func) } return map } 上遇到以下错误:

wrap(actions)

因此,由于某种原因,它与Argument of type '{ first: (id: number) => void; second: (name: string) => void; }' is not assignable to parameter of type 'Record<"first" | "second", never>'. Types of property 'first' are incompatible. Type '(id: number) => void' is not assignable to type 'never'. 不匹配(id: number) => void,因此推断出(...args: any) => any


所以我尝试了一些不同的事情:

never

没有错误,但是我的返回类型为function wrap2<T, K extends keyof T, U extends VarFn> (funcList: Record<K, U>) { function wrapOne<T extends (...args: any)=>any>(fn: T) { return (...args: Parameters<typeof fn>) => { someMagicThingyExtra(fn(args)) } } type FuncMap = Record<K, (...args: Parameters<U>)=>void> let map: FuncMap for (var Key in funcList) { let func = funcList[Key] map[Key] = wrapOne(func) } return map } 是:

wrap2(actions)

...而且我丢失了参数类型,这使尝试包装功能但保留签名(即参数的类型)的整个目的无法实现。

欢迎任何帮助或指导。谢谢!


编辑:

Dragomir提供了完全保留签名(参数的类型和返回类型)的答案。我的用例还需要将返回类型更改为{ first: (...args: any) => void second: (...args: any) => void } ,这是我实现的方式:

void

1 个答案:

答案 0 :(得分:2)

您的通用类型T应该具有一个约束,即它的所有成员都是VarFn类型,您可以很容易地使用T extends Record<keyof T, VarFn>进行约束。由于返回的类型与输入类型map完全相同,因此可以只是类型T

type VarFn = (...args: any) => any

function wrap<T extends Record<keyof T, VarFn>>(funcList: T) {

  // this maps a specific function to a function that does something extra
  function wrapOne<T extends (...args: any) => any>(fn: T): T {
    return ((...args: Parameters<typeof fn>) => {
      return someMagicThingyExtra(fn(args))
    }) as T
  }

  // we iterate through the list and map each function to the one that's doing something extra
  let map = {} as T
  for (var Key in funcList) {
    let func = funcList[Key]
    map[Key] = wrapOne(func)
  }
  return map
}

const actions = {
  first: (id: number) => {/*...*/ },
  second: (name: string) => {/*...*/ }
}
let actionsInterface = wrap(actions)

Playground Link