typescript简单装饰器实现 发表于 2019-01-21 利用typescript简单实现 express的路由装饰器! 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116/** * typescript * 简单实现在express中使用路由装饰器, * @author: dottie * @created at: 2019-01-21 13:24:56 */import "reflect-metadata";const PATH_METADATA = Symbol();const METHOD_METADATA = Symbol();// 这里的target是 class Homefunction Controller(path: string): ClassDecorator { return (target: any) => { Reflect.defineMetadata(PATH_METADATA, path, target); }}// 这里的target是 class Home 的 prototype 等价于 Object.getPrototypeOf(new Home()); 也即为 (new Home()).__proto__function createMethod(method: string) { return (path: string): MethodDecorator => { return (target: any, key: string | symbol, descriptor: any) => { Reflect.defineMetadata(PATH_METADATA, path, target, descriptor.value); Reflect.defineMetadata(METHOD_METADATA, method, target, descriptor.value); } }}const Get = createMethod('get');const Post = createMethod('post');@Controller('/home')class Home { @Get("/data") getData() { return "get data method!"; } @Get('/book') getBook() { return "return book method!"; } @Post('/register') register() { return 'register success!'; }}function isFunction(arg: any): boolean { return typeof arg === 'function';}function isConstructor(arg: string) { return arg === 'constructor';}// 定义RouteInfo类型;interface RouteInfo { rootPath: string, pathInfo: { path: string, method: string, fn: Function, methodName: string }[]}const mapRoute = (instance: Object): RouteInfo => { const prototype = Object.getPrototypeOf(instance); const rootPath = Reflect.getMetadata(PATH_METADATA, prototype.constructor); const properties = Object.getOwnPropertyNames(prototype).filter(item => !isConstructor(item) && isFunction(prototype[item])); let pathInfo = properties.map(item => { const path = Reflect.getMetadata(PATH_METADATA, prototype, prototype[item]); const method = Reflect.getMetadata(METHOD_METADATA, prototype, prototype[item]); return { path, method, fn: prototype[item], methodName: item }; }); return { rootPath, pathInfo };}let r = mapRoute(new Home())console.log(r);/** *{ rootPath: '/home', pathInfo: [ { path: '/data', method: 'get', fn: [Function], methodName: 'getData' }, { path: '/book', method: 'get', fn: [Function], methodName: 'getBook' }, { path: '/register', method: 'post', fn: [Function], methodName: 'register' } ]} */// call methodlet getData = r.pathInfo[0].fn.call(null);console.log(getData); // get data method!let register = r.pathInfo[2].fn.call(null);console.log(register); // register success!