TS 裝飾器(2): 元數(shù)據(jù)
在裝飾器
函數(shù)中 ,我們可以拿到類眶明、方法艰毒、訪問符、屬性搜囱、參數(shù)的基本信息丑瞧,如它們的名稱柑土,描述符等。獲取更多信息就需要通過另外的方式來進(jìn)行:元數(shù)據(jù)
绊汹。
1稽屏、什么是元數(shù)據(jù)?
元數(shù)據(jù):用來描述數(shù)據(jù)的數(shù)據(jù)西乖,在我們的程序中狐榔,對象、類等都是數(shù)據(jù)获雕,它們描述了某種數(shù)據(jù)薄腻。另外還有一種數(shù)據(jù),它可以用來描述 對象届案、類庵楷,這些用來描述數(shù)據(jù)的數(shù)據(jù)就是元數(shù)據(jù)。
在編譯過程中產(chǎn)生的元數(shù)據(jù)是非常重要的信息楣颠,比如在 nestjs 框架中 DI 和 IOC 的實現(xiàn)久依賴了他們尽纽。
2、reflect-metadata
首先童漩,需要安裝 reflect-metadata
弄贿。
2.1、定義元數(shù)據(jù)
我們可以給類矫膨、方法 等數(shù)據(jù)定義元數(shù)據(jù)差凹,元數(shù)據(jù)會被附加到指定的 類、方法等數(shù)據(jù)之上豆拨,但是又不會影響類直奋、方法本身的代碼能庆。
2.2施禾、使用語法
(1) 設(shè)置
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey)
- metadataKey:meta 數(shù)據(jù)的 key
- metadataValue:meta 數(shù)據(jù)的 值
- target:meta 數(shù)據(jù)附加的目標(biāo)
- propertyKey(可選):對應(yīng)的 property key
(2) 獲取
Reflect.getMetadata(metadataKey, target, propertyKey)
import "reflect-metadata";
class A {
public static method1() {}
public method2() {}
}
let obj = new A();
Reflect.defineMetadata("key", 1, A);
Reflect.defineMetadata("key", 2, A, "method1");
Reflect.defineMetadata("key", 3, obj);
Reflect.defineMetadata("key", 4, A, "method2");
console.log(Reflect.getMetadata("key", A));
console.log(Reflect.getMetadata("key", A, "method1"));
console.log(Reflect.getMetadata("key", obj));
console.log(Reflect.getMetadata("key", obj, "method2"));
2.3、裝飾器簡化操作
- 通過
Reflect.defineMetadata
方法調(diào)用來添加元數(shù)據(jù) - 通過
@Reflect.metadata
裝飾器來添加元數(shù)據(jù)
import "reflect-metadata";
@Reflect.metadata("key", 1)
class A {
@Reflect.metadata("key", 2)
public static method1() {}
@Reflect.metadata("key", 4)
public method2() {}
}
let obj = new A();
console.log(Reflect.getMetadata("key", A));
console.log(Reflect.getMetadata("key", A, "method1"));
console.log(Reflect.getMetadata("key", obj));
console.log(Reflect.getMetadata("key", obj, "method2"));
3搁胆、使用 emitDecoratorMetadata
如何知道一個方法中有多少個參數(shù)弥搞,每個參數(shù)的類型是什么呢?tsconfig.json 中有一個配置 emitDecoratorMetadata渠旁,開啟該特性攀例,typescript 會在編譯之后自動給類、方法顾腊、訪問符粤铭、屬性、參數(shù)添加如下幾個元數(shù)據(jù):
-
design:type
:被裝飾目標(biāo)的類型- 裝飾器作用于成員屬性:屬性的標(biāo)注類型
- 裝飾器作用于成員方法:Function 類型
-
design:paramtypes
: 被裝飾目標(biāo)的參數(shù)類型- 裝飾器作用于成員方法:方法形參列表的標(biāo)注類型
- 裝飾器作用于類:構(gòu)造函數(shù)形參列表的標(biāo)注類型
-
design:returntype
- 成員方法:函數(shù)返回值的標(biāo)注類型
3.1杂靶、方法裝飾器實驗
源碼:
function f() {
return function (target: any, name: string, descriptor: PropertyDescriptor) {
console.log(descriptor.value.length);
};
}
class B {
name: string;
constructor(a: string) {
this.name = a;
}
@f()
method(a: string, b: string): string {
return "a";
}
}
產(chǎn)物:
// 太長了梆惯,隱藏實現(xiàn)
var __decorate = function () {}
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
function f() {
return function (target, name, descriptor) {
console.log(descriptor.value.length);
};
}
var B = /** @class */ (function () {
function B(a) {
this.name = a;
}
B.prototype.method = function (a, b) {
return "a";
};
__decorate([
f(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", String)
], B.prototype, "method", null);
return B;
}());
3.2酱鸭、類裝飾器實驗
@testable
class MyTestableClass {
constructor (name: string, age: number) {}
}
function testable(target: Function) {
(target as any).isTestable = true;
}
(MyTestableClass as any).isTestable // true
產(chǎn)物:
var MyTestableClass = /** @class */ (function () {
function MyTestableClass(name, age) {
}
MyTestableClass = __decorate([
testable,
__metadata("design:paramtypes", [String, Number])
], MyTestableClass);
return MyTestableClass;
}());
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable; // true