Typescript - 在保留签名的同时包装函数
Posted
技术标签:
【中文标题】Typescript - 在保留签名的同时包装函数【英文标题】:Typescript - wrapping functions while preserving signatures 【发布时间】:2020-03-31 10:04:51 【问题描述】:我试图弄清楚如何包装定义的函数,以便在保留其签名的同时做额外的工作。这是想要的效果:
程序员定义接口:
const actions =
first: (id: number) => /*...*/,
second: (name: string) => /*...*/
let actionsInterface = wrap(actions)
export actionsInterface
actionsInterface
应该(即目标)具有以下界面:
first: (id: number) => void,
second: (name: string) => void
它基本上提供了与第一次定义相同的接口(即相同的函数列表,具有相同的参数,不计算返回类型),但还有一些额外的工作正在进行中,由@987654324 注入@。
我目前的实现是这样的:
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)
返回类型是:
first: (...args: any) => void
second: (...args: any) => void
...我丢失了参数类型,这违背了尝试包装功能但保留签名(即参数类型)的整个目的。
欢迎任何帮助或指导。谢谢!
编辑:
Dragomir 提供了完全保留签名(参数类型和返回类型)的答案。我的用例还需要将返回类型更改为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
【问题讨论】:
【参考方案1】:你的泛型类型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
【讨论】:
我印象深刻的是 if 设法推断出正确的函数类型,而不是将它们保留为VarFn
。我猜想扩展整个 Record
而不仅仅是它的值部分就可以了。这确实解决了我的问题。再一次,我返回的函数应该在包装后返回void
。那怎么办?谢谢。
我成功地将返回类型更改为void
。编辑过的问题也包含在内,它基于您的回答。谢谢!以上是关于Typescript - 在保留签名的同时包装函数的主要内容,如果未能解决你的问题,请参考以下文章
在 Sphinx 文档中保留包装/装饰 Python 函数的默认参数
如何在 javascript/typescript 事件回调中访问它,同时保留 removeEventListener 的能力? [复制]