Typescript - 在保留签名的同时包装函数

Typescript - wrapping functions while preserving signatures

我正在尝试弄清楚如何包装已定义的函数,以便我可以在保留其签名的同时做额外的工作。这是预期的效果:

程序员定义接口:

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

actionsInterface 应该(即这就是目标)具有以下接口:

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

它基本上提供了与最初定义的完全相同的接口(即相同的函数列表,具有相同的参数,不包括 return 类型),但还有一些额外的工作正在完成,即由 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
}

没有错误,但是我的 return 类型 wrap2(actions) 是:

{
    first: (...args: any) => void
    second: (...args: any) => void
}

...我丢失了参数类型,这违背了尝试包装功能但保留签名(即参数类型)的全部目的。

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


编辑:

Dragomir 提供的答案完全保留了签名(参数类型和 return 类型)。我的用例还需要将 return 类型更改为 void,这就是我实现它的方式:

function wrap<T extends Record<keyof T, (...args: any)=>any>>(funcList: T) {

    // 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>): void => {
            someMagicThingyExtra(fn(args))
        })
    }

    // we iterate through the list and map each function to the one that's doing something extra
    type WrapMap = {
        [K in keyof T]: (...args: Parameters<T[K]>)=>void
    }
    let map: WrapMap
    for (var Key in map) {
        map[Key] = wrapOne(funcList[Key])
    }
    return map
}

你的泛型类型 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

这是我登陆的第一件事。它甚至保留了包装函数的泛型:

const newFunction = ((one, two) => {
    const result = oldFunction(one, two)
    // ...
    return result
}) as typeof oldFunction