typescript简单装饰器实现

利用typescript简单实现 express的路由装饰器!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* 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 Home
function 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 method
let 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!