LoginSignup
0
0

More than 1 year has passed since last update.

【TypeScript】reflect-metadataを用いてDI

Last updated at Posted at 2023-01-08

はじめに

以下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は一区切りつきました。
あざました。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0