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.ts
import '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
```typescript
import 'reflect-metadata';
type Constructor<T = any> = new (...args: any) => T
type Decorator = ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator
type Token = string | symbol | Constructor | Function
interface IProvider {
type: 'class' | 'value'
value: any
instance?: 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 } = this
if (map.has(token)) {
const provider = map.get(token)!
if (provider.type === 'value') {
return provider.value
}
if (provider.type === 'class') {
if(provider.instance) return provider.instance
const instance = new provider.value()
provider.instance = instance
return 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 或者用类作key
container.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!: string
test() {
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
// ================