-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.ts
More file actions
83 lines (68 loc) · 2.27 KB
/
index.ts
File metadata and controls
83 lines (68 loc) · 2.27 KB
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
/// <reference path="node_modules/reflect-metadata/reflect-metadata.d.ts" />
import 'reflect-metadata'
interface Newable<T> {
new(...args: any[]): T
name?: string
}
interface InjectableOverride {
key: Newable<any>
val: Newable<any>
}
interface Dict<T> {
[idx: string]: T
}
let injectableId: number = 1
const injectableTypeKey: string = '__injectableTypeKey__'
class Injector {
private cache: Dict<any>
private explicitBindings: Dict<Newable<any>>
constructor(bindingOverrides: InjectableOverride[] = []) {
this.cache = {}
this.explicitBindings = {}
bindingOverrides.forEach((override: InjectableOverride) => {
let key: string = override.key[injectableTypeKey]
this.explicitBindings[key] = override.val
})
}
getInstance<T>(newable: Newable<T>): T {
let newableTypeKey: string = newable[injectableTypeKey]
let cachedInstance: T = this.cache[newableTypeKey]
if (cachedInstance) {
return cachedInstance
}
if (this.explicitBindings[newableTypeKey]) {
newable = this.explicitBindings[newableTypeKey]
}
let dependencies: Newable<any>[] = Reflect.getMetadata('design:paramtypes', newable)
let args: any[] = []
if (dependencies && dependencies.length) {
args = dependencies.map((dep: Newable<any>) => {
if (dep === undefined) {
throw new Error(`Cannot inject '${newable.name}' because there is no type metadata for a dependency.
you might have a circular dependency`)
}
return this.getInstance(dep)
})
}
let isDecoratedWithInject: boolean = Reflect.getMetadata('DI:injectable', newable)
if (!isDecoratedWithInject) {
throw new Error(`Cannot inject '${newable.name}' because it is not decorated with @Injectable.`)
}
let applyArgs: Newable<any>[] = [newable]
applyArgs = applyArgs.concat(args)
let constructor: Newable<T> = Function.prototype.bind.apply(newable, applyArgs)
let newInstance: T = new constructor()
this.cache[newableTypeKey] = newInstance
return newInstance
}
}
function Injectable<T>(target: Newable<T>): Newable<T> {
Reflect.defineMetadata('DI:injectable', true, target)
target[injectableTypeKey] = injectableId++
return target
}
export {
Injectable as default,
Injector,
InjectableOverride
}