はじめに
以下2つの続きです。
限界を試したくてvanillaで頑張りましたが、難しかったので一度reflect-metadataを使ってDIを実現しました。
一番折れた理由は後述します。
tsconfig
この2つを有効化してください。
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
}
}
emitDecoratorMetadata
というパラメータがあり、これがないとreflect-metadataが上手く動いてくれなかったのが折れた理由です。
実装
import 'reflect-metadata';
type constructor<T> = {
new (...args: any[]): T;
};
class DependencyInjection {
private static instance: DependencyInjection;
container = new Map<constructor<any>, constructor<any>[]>();
instance = new Map<any, constructor<any>>();
constructor() {}
static register(target: constructor<any>, params: any[]): void {
if (!DependencyInjection.instance) {
DependencyInjection.instance = new DependencyInjection();
}
DependencyInjection.instance.container.set(target, params);
}
resolve() {
const keys = Array.from(DependencyInjection.instance.container.keys());
keys.forEach((ctor) => {
const targetDependencies = DependencyInjection.instance.container.get(ctor);
if (targetDependencies == null || targetDependencies.length === 0) {
this.instance.set(ctor, new ctor());
} else {
const instances = targetDependencies
.map(cls => this.instance.get(cls))
.filter((x): x is constructor<any> => x != null);
this.instance.set(ctor, new ctor(...instances));
}
})
}
}
const injectable = () => {
return (target: constructor<any>): void => {
const params: any[] = Reflect.getMetadata('design:paramtypes', target) || [];
DependencyInjection.register(target, params);
};
};
使ってみる
@injectable()
class A {
constructor() {}
}
@injectable()
class B {
a: A;
constructor(a: A) {
this.a = a;
}
}
@injectable()
class C {
a: A;
constructor(a: A) {
this.a = a;
}
}
@injectable()
class D {
b: B;
constructor(b: B) {
this.b = b;
}
}
@injectable()
class E {
c: C;
d: D;
constructor(c: C, d: D) {
this.c = c;
this.d = d;
}
}
@injectable()
class F {
b: B;
e: E;
constructor(b: B, e: E) {
this.b = b;
this.e = e;
}
}
const dependencyInjection = new DependencyInjection();
dependencyInjection.resolve();
console.log(dependencyInjection.instance);
実行結果
Map(6) {
[class A] => A {},
[class B] => B { a: A {} },
[class C] => C { a: A {} },
[class D] => D { b: B { a: A {} } },
[class E] => E { c: C { a: A {} }, d: D { b: [B] } },
[class F] => F { b: B { a: A {} }, e: E { c: [C], d: [D] } }
}
終わりに
自分の中で、これでDIは一区切りつきました。
あざました。