declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
类中不同声明上的装饰器将按以下规定的顺序应用:
参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个实例成员。
参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个静态成员。
参数装饰器应用到构造函数。
类装饰器应用到类。
当存在多个装饰器来装饰同一个声明时,则会有以下的顺序:
- 首先,由上至下依次对装饰器表达式求值,得到返回的真实函数(如果有的话)
- 而后,求值的结果会由下至上依次调用 (有点类似洋葱模型)
```typescript
function foo() {
console.log(“foo in”); // 1
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
} }console.log("foo out"); // 4
function bar() { console.log(“bar in”); // 2 return function (target, propertyKey: string, descriptor: PropertyDescriptor) { console.log(“bar out”); // 3 } }
class A { @foo() @bar() method() {} }
// foo in // bar in // bar out // foo out
<a name="cuUIK"></a># Reflect Metadata<a name="AsWn8"></a>## 使用条件目前想要使用,我们还需要安装`reflect-metadata`与在`tsconfig.json`中启用`emitDecoratorMetadata`选项```typescript// 入口文件 index.tsimport 'reflect-metadata';
// tsconfig.json{"compilerOptions": {"experimentalDecorators": true,"emitDecoratorMetadata": true,}}
为类或类属性添加了元数据后,构造函数的原型(或是构造函数,根据静态成员还是实例成员决定)会具有[[Metadata]]属性,该属性内部包含一个Map结构,键为属性键,值为元数据键值对。
Reflect.defineMetadata(metadataKey, metadataValue, target[, propertyKey])// 其数据结构可表示如下:WeakMap {target: Map {propertyKey: Map {metadataKey: metadataValue}}}
内置元数据
在 tsconfig.json 中开启了 emitDecoratorMetadata 选项,此时,TypeScript 在编译时定义一些 元数据设计键(TypeScript 夹带的私货,目前babel并没有此feature),目前可用的有:
- 属性类型元数据
design:type:Reflect.getMetadata("design:type", target, key)用于获取类属性的类型 - 参数类型元数据
design:paramtypes:Reflect.getMetadata("design:paramtypes", target, key)用于获取方法参数的类型 - 返回类型元数据
design:returntype:Reflect.getMetadata("design:returntype", target, key)用于获取返回值的类型``typescript const MyClassDecorator: ClassDecorator = (target: any) => { const type = Reflect.getMetadata('design:type', target); console.log(类[${target.name}] design:type = ${type && type.name}); const paramTypes: any[] = Reflect.getMetadata('design:paramtypes', target); console.log(类[${target.name}] design:paramtypes =, paramTypes && paramTypes.map(item => item.name)); const returnType = Reflect.getMetadata('design:returntype', target) console.log(类[${target.name}] design:returntype = ${returnType && returnType.name}); }; // @ts-ignore const MyPropertyDecorator: PropertyDecorator = (target: any, key: string) => { const type = Reflect.getMetadata('design:type', target, key); console.log(属性[${key}] design:type = ${type && type.name}); const paramTypes: any[] = Reflect.getMetadata('design:paramtypes', target, key); console.log(属性[${key}] design:paramtypes =, paramTypes && paramTypes.map(item => item.name)); const returnType = Reflect.getMetadata('design:returntype', target, key); console.log(属性[${key}] design:returntype = ${returnType && returnType.name}); }; // @ts-ignore const MyMethodDecorator: MethodDecorator = (target: any, key: string, descriptor: PropertyDescriptor) => { const type = Reflect.getMetadata('design:type', target, key); console.log(方法[${key}] design:type = ${type && type.name}); const paramTypes: any[] = Reflect.getMetadata('design:paramtypes', target, key); console.log(方法[${key}] design:paramtypes =, paramTypes && paramTypes.map(item => item.name)); const returnType = Reflect.getMetadata('design:returntype', target, key) console.log(方法[${key}] design:returntype = ${returnType && returnType.name}); }; // @ts-ignore const MyParameterDecorator: ParameterDecorator = (target: any, key: string, paramIndex: number) => { const type = Reflect.getMetadata('design:type', target, key); console.log(参数[${key} - ${paramIndex}] design:type = ${type && type.name}); const paramTypes : any[] = Reflect.getMetadata('design:paramtypes', target, key); console.log(参数[${key} - ${paramIndex}] design:paramtypes =, paramTypes && paramTypes.map(item => item.name)); const returnType = Reflect.getMetadata('design:returntype', target, key) console.log(参数[${key} - ${paramIndex}] design:returntype = ${returnType && returnType.name}); }; @MyClassDecorator class MyClass { @MyPropertyDecorator myProperty: string; constructor (myProperty: string) { this.myProperty = myProperty; } @MyMethodDecorator myMethod (@MyParameterDecorator index: number, name: string): string { return${index} - ${name}`; } }
// 结果 // 属性[myProperty] design:type = String // 属性[myProperty] design:paramtypes = undefined // 属性[myProperty] design:returntype = undefined // 参数[myMethod - 0] design:type = Function // 参数[myMethod - 0] design:paramtypes = [ ‘Number’, ‘String’ ] // 参数[myMethod - 0] design:returntype = String // 方法[myMethod] design:type = Function // 方法[myMethod] design:paramtypes = [ ‘Number’, ‘String’ ] // 方法[myMethod] design:returntype = String // 类[MyClass] design:type = undefined // 类[MyClass] design:paramtypes = [ ‘String’ ] // 类[MyClass] design:returntype = undefined
<a name="5Iarf"></a>## **基本类型序列化**- `number` 序列化为 `Number`- `string` 序列化为 `String`- `boolean` 序列化为 `Boolean`- `any` 序列化为 `Object`- `void` 序列化为 `undefined`- `Array` 序列化为 `Array`- 如果是`Tuple`,序列化为`Array`- 如果`class`将它序列化为类构造函数- 如果`Enum`序列化为`Number`或者`String`- 如果至少有一个呼叫签名,则序列化为 `Function`- 否则序列化为`Object`(包括接口)<a name="mnOBB"></a>## 简单实现IOC```typescriptimport 'reflect-metadata';type Constructor<T = any> = new (...args: any) => Ttype Decorator = ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecoratortype Token = string | symbol | Constructor | Functioninterface IProvider {type: 'class' | 'value'value: anyinstance?: any}const TYPE = 'design:type'const PARAMTYPES = 'design:paramtypes'const RETURNTYPE = 'design:returntype'// const RETURNTYPE = 'ioc:key'class Provider implements IProvider {constructor(public readonly type: 'class' | 'value', public readonly value: any) {}}class Container {private map = new Map<Token, IProvider>()inject(token: Token, provider: IProvider) {this.map.set(token, provider)}get<T>(token: Token): T {const { map } = thisif (map.has(token)) {const provider = map.get(token)!if (provider.type === 'value') {return provider.value}if (provider.type === 'class') {if(provider.instance) return provider.instanceconst instance = new provider.value()provider.instance = instancereturn instance}throw new Error('未知type'+ provider.type)} else {console.log('================');console.log('providers >>>>>>', map);console.log('================');throw new Error('找不到' + token.toString())}}}const container = new Container();container.inject('val', new Provider('value', 'val'))function Injectable(token?: Token): ClassDecorator {return (target) => {console.log('================');console.log('target >>>>>>', target);console.log('================');// 用token 或者用类作keycontainer.inject(token ?? target, new Provider('class', target))}}function Value(token?: Token): PropertyDecorator {return (target, propertyKey) => {Object.defineProperty(target, propertyKey, {get() {return container.get(token ?? propertyKey)}})}}function Inject(token?: Token): PropertyDecorator {return (target, propertyKey) => {const type = Reflect.getMetadata(TYPE, target, propertyKey);console.log('================');console.log('type >>>>>>', type);console.log('================');Object.defineProperty(target, propertyKey, {get() {return container.get(token ?? type)}})}}interface ITest {test(): void}const ITest = Symbol('ITest')@Injectable(ITest) // 至少要有一个装饰器class Test implements ITest{@Value('val')private readonly valTest!: stringtest() {console.log('================');console.log('valTest is', this.valTest);console.log('================');}}@Injectable()class TestMachine {@Inject(ITest)readonly test!: Test;run() {this.test.test()}}const testMachine = new TestMachine();testMachine.run()// ================// target >>>>>> [Function: Test]// ================// ================// type >>>>>> [Function: Test]// ================// ================// target >>>>>> [Function: TestMachine]// ================// ================// valTest is val// ================
