如何在 TypeScript 中使装饰器类型安全?
Posted
技术标签:
【中文标题】如何在 TypeScript 中使装饰器类型安全?【英文标题】:How do I make a decorator typesafe in TypeScript? 【发布时间】:2021-01-24 22:22:40 【问题描述】:例子:
class Parent
parentMethod()
// ...
@Hooks(
// Only methods from the `Child` class (including inherited methods) must be allowed here
childMethod: [/* ... */],
parentMethod: [/* ... */]
)
class Child extends Parent
childMethod()
// ...
@Hooks()
装饰器将对象作为参数。在这个对象中,键是来自Child
类的方法名。如何使 @Hooks()
装饰器类型安全?
您能否提供一个包含@Hooks()
类型的代码示例?
【问题讨论】:
【参考方案1】:如果它适合你,请检查它
interface ClassType<T> extends Function
new (...args: any[]): T;
type MethodKeys<T> = (
[K in keyof T]: T[K] extends Function ? K : never
)[keyof T]
type HooksOptions<T> =
[K in MethodKeys<T>]: []
function Hooks<T>(_options: HooksOptions<T>)
return function (ctor: ClassType<T>): ClassType<T>
return ctor
class Parent
parentMethod()
// ...
@Hooks<Child>(
childMethod: [/* ... */],
parentMethod: [/* ... */]
)
class Child extends Parent
childMethod()
// ...
@Hooks("invalid-value") // error
class Child2 extends Parent
childMethod()
// ...
@Hooks<Child3>(
c: [], // error
childMethod: [/* ... */],
childMethod2: [/* ... */], // error
parentMethod: [/* ... */]
)
class Child3 extends Parent
public c: string = ''
childMethod()
// ...
Playground
【讨论】:
我喜欢你的解决方案,但如果没有指定@Hooks<GenericType>
的泛型类型,TypeScript 不会强制你指定它,@Hooks
将接受 any 参数为有效。见example
@Mike 回答已更新。 TS 现在将强制通过泛型【参考方案2】:
打字稿中某个类的方法没有类型(在其他语言中也没有听说过这种类型以及TBH), 你可以给函数签名一个类型。
编辑: 显然有一些我不知道的技巧 ,如其他答案所示.. 但是,如果您对快速简单但有限的解决方案感兴趣:
例如:
let myAdd: (baseValue: number, increment: number) => number = (baseValue, increment) => baseValue + increment
更多关于类型函数的信息: https://www.typescriptlang.org/docs/handbook/functions.html
如果足够了,就在装饰器的声明中使用类型签名,如下所示:
Interface ObjectInterface
ChildMethods: (param) => void [], //or whatever fuction signature meets your need, this is just an example.
ParentMethods: (param) => void[]
//this is the decorator declaration
function Hooks(obj: ObjectInterface)
return function (
target,
propertyKey: string,
descriptor: PropertyDescriptor
)
// what ever implementation you have in hooks...
;
这种方式在将不属于(param) => void
类型的函数传递给装饰器时会导致编译错误。
更多信息在这里: https://www.typescriptlang.org/docs/handbook/decorators.html
这里是类型化函数数组,就像我在 ObjectInterface
上所做的那样:
A Typed array of functions
【讨论】:
【参考方案3】:我找到了解决办法
interface Type<T> extends Function
new (...args: any[]): T;
type HookMap<T> =
[func in keyof T]?: any[];
;
function Hooks<T>(hookMap: HookMap<T>)
return (clazz: Type<T>): void =>
// ...
;
class Parent
parentMethod()
// ...
@Hooks(
childMethod: [/* ... */],
parentMethod: [/* ... */]
)
class Child extends Parent
childMethod()
// ...
Playground
【讨论】:
好吧,这将允许在 Child 和 Parent 类中声明的属性在装饰器对象中使用。在问题中,解决方案应该只包含方法名称。 @ShivamSingla 是的,我同意。但我无法将您的MethodKeys
类型集成到我的解决方案中。见example以上是关于如何在 TypeScript 中使装饰器类型安全?的主要内容,如果未能解决你的问题,请参考以下文章
TypeScript React HOC 返回类型与装饰器兼容