中间唯一用到reflect-matadata类

hasKey函数,在ts中需要这个函数加强ts类型识别

controller装饰器帮助Controller添加参数实例,这里参数是indexService,前加注解inject,在Controller类的constructor调用时,能取到inject注入的service实例,再将这个实例添加到该Controller的私有变量service中生成实例,在其它方法中直接通过this.indexService调用

inject将类型定义到反射当中,定义时从container中取类型实例放到反射中

container在创建时绑定类型的key和实例到其map当中

1.Controller.ts

import { inject, TYPES } from "./Container";
import { IIndexService } from "./IndexService";
import 'reflect-metadata'

function getParam(str: string) {
    /\\((.*)\\)/.test(str);
    return RegExp.$1.split(',');
}
//不熟
function hasKey<O extends Object>(key: PropertyKey, o: O): key is keyof O {
    return o.hasOwnProperty(key)
}
//不熟
function controller<T extends new (...args: any[]) => {}>(constructor: T) {
    class Controller extends constructor {
        constructor(...args: any[]) {
            super(...args);
            let params = getParam(constructor.toString());
            for (let identity of params) {
                if (hasKey(identity, this)) {
                    this[identity] = Reflect.getMetadata(TYPES[identity], constructor)
                }
            }
        }
    }
    return Controller;
}

@controller
class IndexController {
    private indexService: IIndexService
    constructor(@inject(TYPES['indexService'])indexService?: IIndexService) {
        this.indexService = <IIndexService>indexService;
    }
    log() {
        this.indexService.info('hello world');
    }
}

let instance = new IndexController();
instance.log()

2.IndexService.ts

export interface IIndexService {
    info(str: string): void
}
export class IndexService implements IIndexService {
    info(str: string): void {
        console.log(str)
    }
}

3.Container.ts

import { IndexService } from "./IndexService";
export class createIoc {
    private container: Map<Symbol, Object> = new Map<Symbol, Object>()
    get(key: Symbol) {
        let item = this.container.get(key)
        if (item) {
            return item
        } else {
            throw Error('...')
        }
    }
    bind(key: Symbol, cb: Function) {
        if (!this.container.has(key)) {
            let item = cb();
            this.container.set(key, item)
        }
    }
}
interface ITYPE {
    [key: string]: Symbol
}
//不熟
export function inject(serviceIdentifier: Symbol) {
    return function (target: Function, targetKey: string, index: number) {
        if (!targetKey) {
            Reflect.defineMetadata(serviceIdentifier, container.get(serviceIdentifier), target)
        }
    }
}
export const TYPES: ITYPE = {
    'indexService': Symbol.for('indexService')
}
const container = new createIoc();
container.bind(TYPES['indexService'], () => new IndexService)
export { container }