-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
125 lines (114 loc) · 3.38 KB
/
index.ts
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
117
118
119
120
121
122
123
124
125
type RawType = 'String' | 'Number' | 'BigInt' | 'Boolean' | 'Null' | 'Undefined' | 'Object' | 'Array' | 'Set' | 'Map' | 'Date' | 'RegExp' | 'URL' | 'Function' | 'Promise' | 'Error'
interface PromiseMethods {
then: Function
catch: Function
finally: Function
}
export interface Thenable extends PromiseMethods {
value: any
}
function isSymbol(val: unknown): val is symbol {
return typeof val === 'symbol'
}
function isFunction(val: unknown): val is Function {
return typeof val === 'function'
}
function getTypeString(value: unknown): string {
return Object.prototype.toString.call(value)
}
function getRawType(value: unknown): RawType {
return getTypeString(value).slice(8, -1) as RawType
}
function createThenable(value: any, rawType: string): Thenable {
return {
then(onfulfilled?: Function | undefined | null, onrejected?: Function | undefined | null) {
if (rawType === 'Promise') {
return proxyThenable(value.then(onfulfilled, onrejected))
}
else if (rawType === 'ThrowError' && onrejected) {
try {
const result = onrejected(value)
return proxyThenable(result)
}
catch (error) {
return proxyThenable(error, 'ThrowError')
}
}
else if (rawType !== 'ThrowError' && onfulfilled) {
try {
const result = onfulfilled(value)
return proxyThenable(result)
}
catch (error) {
return proxyThenable(error, 'ThrowError')
}
}
else {
return proxyThenable(value, rawType)
}
},
catch(onrejected?: Function | undefined | null) {
if (rawType === 'Promise') {
return proxyThenable(value.catch(onrejected))
}
else if (rawType === 'ThrowError' && onrejected) {
try {
const result = onrejected(value)
return proxyThenable(result)
}
catch (error) {
return proxyThenable(error, 'ThrowError')
}
}
else {
return proxyThenable(value, rawType)
}
},
finally(onfinally?: Function | undefined | null) {
if (rawType === 'Promise') {
return proxyThenable(value.finally(onfinally))
}
else {
onfinally && onfinally()
return proxyThenable(value, rawType)
}
},
value,
}
}
const RAW_TYPE = Symbol('RAW_TYPE')
const IS_PROXY = Symbol('IS_PROXY')
function proxyThenable(target: any, rawType?: string): any {
if (isFunction(target) && target[IS_PROXY])
return target
const fn = () => target
fn[RAW_TYPE] = rawType || getRawType(target)
fn[IS_PROXY] = true
return new Proxy(fn, {
get(target, p, receiver) {
if (isSymbol(p) && [RAW_TYPE, IS_PROXY].includes(p)) {
return Reflect.get(target, p, receiver)
}
else {
const result = target()
if (target[RAW_TYPE] === 'Function' || isSymbol(p) || !['then', 'catch', 'finally', 'value'].includes(p)) {
return Reflect.get(result, p, receiver)
}
else {
return createThenable(result, target[RAW_TYPE])[p as keyof Thenable]
}
}
},
apply(target, thisArg, argArray) {
const result = target()
try {
const value = Reflect.apply(result, thisArg, argArray)
return proxyThenable(value)
}
catch (error) {
return proxyThenable(error, 'ThrowError')
}
},
})
}
export default proxyThenable