在 TypeScript 中,装饰器就是可以添加到类及其成员的函数。TypeScript 装饰器可以注释和修改类声明、方法、属性和访问器。Decorator类型定义如下:
type Decorator = (target: Input, context: { kind: string; name: string | symbol; access: { get?(): unknown; set?(value: unknown): void; }; private?: boolean; static?: boolean; addInitializer?(initializer: () => void): void;}) => Output | void;
上面的类型定义解释如下:
接下来,我们就来了解一下装饰器的各种类型。
当将函数作为装饰器附加到类时,将收到类构造函数作为第一个参数:
type ClassDecorator = (value: Function, context: { kind: "class" name: string | undefined addInitializer(initializer: () => void): void}) => Function | void
例如,假设想要使用装饰器向 Rocket 类添加两个属性:fuel 和 isEmpty()。在这种情况下,可以编写以下函数:
function WithFuel(target: typeof Rocket, context): typeof Rocket { if (context.kind === "class") { return class extends target { fuel: number = 50 isEmpty(): boolean { return this.fuel == 0 } } }}
在确保装饰元素的类型确实是类之后,返回一个具有两个附加属性的新类。或者,可以使用原型对象来动态添加新方法:
function WithFuel(target: typeof Rocket, context): typeof Rocket { if (context.kind === "class") { target.prototype.fuel = 50 target.prototype.isEmpty = (): boolean => { return this.fuel == 0 } }}
可以按以下方式使用 WithFuel:
@WithFuelclass Rocket {}const rocket = new Rocket()console.log((rocket as any).fuel)console.log(`empty? ${(rocket as any).isEmpty()}`)/* Prints:50empty? false*/
可以看到,这里将rocket转换为any类型才能访问新的属性。这是因为装饰器无法影响类型的结构。
如果原始类定义了一个稍后被装饰的属性,装饰器会覆盖原始值。例如,如果Rocket有一个具有不同值的fuel属性,WithFuel装饰器将会覆盖该值:
function WithFuel(target: typeof Rocket, context): typeof Rocket { if (context.kind === "class") { return class extends target { fuel: number = 50 isEmpty(): boolean { return this.fuel == 0 } } }}@WithFuelclass Rocket { fuel: number = 75}const rocket = new Rocket()console.log((rocket as any).fuel)// 50
方法装饰器可以用于装饰类方法。在这种情况下,装饰器函数的类型如下:
type ClassMethodDecorator = (target: Function, context: { kind: "method" name: string | symbol access: { get(): unknown } static: boolean private: boolean addInitializer(initializer: () => void): void}) => Function | void
如果希望在调用被装饰的方法之前或之后执行某些操作时,就可以使用方法装饰器。
例如,在开发过程中,记录对特定方法的调用或在调用之前/之后验证前置/后置条件可能非常有用。此外,我们还可以影响方法的调用方式,例如通过延迟其执行或限制在一定时间内的调用次数。
最后,可以使用方法装饰器将一个方法标记为已废弃,并记录一条消息来警告用户,并告知他们应该使用哪个方法代替:
function deprecatedMethod(target: Function, context) { if (context.kind === "method") { return function (...args: any[]) { console.log(`${context.name} is deprecated and will be removed in a future version.`) return target.apply(this, args) } }}
在这种情况下,deprecatedMethod函数的第一个参数是要装饰的方法。确认它确实是一个方法后(context.kind === "method"),返回一个新的函数,该函数在调用实际方法之前包装被装饰的方法并记录一条警告消息。
接下来,可以按照以下方式使用装饰器:
@WithFuelclass Rocket { fuel: number = 75 @deprecatedMethod isReadyForLaunch(): Boolean { return !(this as any).isEmpty() }}const rocket = new Rocket()console.log(`Is ready for launch? ${rocket.isReadyForLaunch()}`)
在isReadyForLaunch()方法中,引用了通过WithFuel装饰器添加的isEmpty方法。注意,必须将其转换为any类型的实例,与之前一样。当调用isReadyForLaunch()方法时,会看到以下输出,显示警告消息被正确地打印出来:
isReadyForLaunch is deprecated and will be removed in a future version.Is the ready for launch? true
属性装饰器与方法装饰器的类型非常相似:
type ClassPropertyDecorator = (target: undefined, context: { kind: "field" name: string | symbol access: { get(): unknown, set(value: unknown): void } static: boolean private: boolean}) => (initialValue: unknown) => unknown | void
属性装饰器的用例与方法装饰器的用法也非常相似。例如,可以跟踪对属性的访问或将其标记为已弃用:
function deprecatedProperty(_: any, context) { if (context.kind === "field") { return function (initialValue: any) { console.log(`${context.name} is deprecated and will be removed in a future version.`) return initialValue } }}
代码与为方法定义的 deprecatedMethod 装饰器非常相似,它的用法也是如此。
与方法装饰器非常相似的是访问器装饰器,它是针对 getter 和 setter 的装饰器:
type ClassSetterDecorator = (target: Function, context: { kind: "setter" name: string | symbol access: { set(value: unknown): void } static: boolean private: boolean addInitializer(initializer: () => void): void}) => Function | voidtype ClassGetterDecorator = (value: Function, context: { kind: "getter" name: string | symbol access: { get(): unknown } static: boolean private: boolean addInitializer(initializer: () => void): void}) => Function | void
访问器装饰器的定义与方法装饰器的定义类似。例如,可以将 deprecatedMethod 和 deprecatedProperty 修饰合并到一个已弃用的函数中,该函数也支持 getter 和 setter:
function deprecated(target, context) { const kind = context.kind const msg = `${context.name} is deprecated and will be removed in a future version.` if (kind === "method" || kind === "getter" || kind === "setter") { return function (...args: any[]) { console.log(msg) return target.apply(this, args) } } else if (kind === "field") { return function (initialValue: any) { console.log(msg) return initialValue } }}
上面介绍了装饰器是什么以及如何正确使用它们,下面来看看装饰器可以帮助我们解决的一些具体问题。
假设想要估计运行一个函数需要多长时间,以此来衡量应用的性能。可以创建一个装饰器来计算方法的执行时间并将其打印在控制台上:
class Rocket { @measure launch() { console.log("3... 2... 1...
本文链接:http://www.28at.com/showinfo-26-5177-0.htmlTypeScript 装饰器实用指南!
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
下一篇: CSS 渐变中的颜色空间和色相插值