ts reflect to define api(like retrofit

tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "../",
"experimentalDecorators":true,
"emitDecoratorMetadata":true,
"target": "ES5",
// "module": "es2015",
"module": "CommonJS",
"types": [
"reflect-metadata"
]
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}

code

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
import 'reflect-metadata';

const METHOD_METADATA = 'method'
const PATH_METADATA = 'path'

const Controller = (path: string): ClassDecorator => {
return target => {
Reflect.defineMetadata(PATH_METADATA, path, target)
}
}

const createMethodDecorator = (method: string) => (path: string): MethodDecorator => {
return (target, key, descriptor) => {
Reflect.defineMetadata(PATH_METADATA, path, descriptor.value!);
Reflect.defineMetadata(METHOD_METADATA, method, descriptor.value!);
}
}
const Get = createMethodDecorator('GET');
const Post = createMethodDecorator('POST');

@Controller('/article')
class Home {
@Get('/getmethod')
someGetMethod() {
return 'hello world';
}
@Post('/postmethod')
somePostMethod(strin: string) { console.log('===') }
}

// tools function
function isConstructor(symbol: any): boolean {
return notUndefined(symbol) &&
symbol instanceof Function &&
symbol.constructor &&
symbol.constructor instanceof Function &&
notUndefined(new symbol) &&
Object.getPrototypeOf(symbol) !== Object.prototype &&
symbol.constructor !== Object &&
symbol.prototype.hasOwnProperty('constructor');
};

function notUndefined(item: any): boolean {
return item != undefined && item != 'undefined';
}

function isFunction(value: any): value is Function {
return typeof value === 'function';
}

function getRouteInfo(instance: Object) {
const prototype = Object.getPrototypeOf(instance);
const methodNames = Object.getOwnPropertyNames(prototype)
.filter(item => !isConstructor(item) && isFunction(prototype[item]))
return methodNames.map(methodName => {
const fn = prototype[methodName];
const route = Reflect.getMetadata(PATH_METADATA, fn);
const method = Reflect.getMetadata(METHOD_METADATA, fn);
return {
methodName,
method,
route,
fn,
}
});
}

console.log(getRouteInfo(new Home()));

output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[
{
methodName: 'constructor',
method: undefined,
route: '/article',
fn: [Function: Home]
},
{
methodName: 'someGetMethod',
method: 'GET',
route: '/getmethod',
fn: [Function]
},
{
methodName: 'somePostMethod',
method: 'POST',
route: '/postmethod',
fn: [Function]
}
]
Author: Rick
Link: https://rcrick.github.io/2020/04/22/ts-reflect-to-define-api-like-retrofit/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
  • 支付寶