66221


最近、TypeScriptを学び始めた者です。

実現したいことは、渡した文字列を基に動的にクラスのインスタンスを作成することなのですが、TypeScript
(Node.js)で表現する方法が分からずに困っております。

Ruby(Rails)では`class_name.constantize.new`、Javaでは`Class.forName(className).newInstance()`  
のような形で実現できるかと思うのですが、TypeScript (Node.js)で実現する方法がございましたらご教示いただければ幸いです。

[code]

    className: string = 'Foo';
    const foo: Foo = /* new 'Foo'(args) のようなことがしたい */;
    
[/code]

* * *

> 何故このようなことがしたくなったのかを追記していただけると、もっと良い方法が提案できるかもしれません

とのコメントをいただいたため以下追記です。

なぜこのようなことがしたくなったかと言うと、TypeScriptを使ってデザインパターンの勉強をしており、Astract
Factoryパターンの実装をしようとしていた時に、以下のように`Factory`クラスを継承している`'ListFactory'`や`'TableFactory'`のような文字列を受け取って動的にインスタンスを作りたいという要求が出てきたためです。

（以下、関連箇所のみ抜粋。あくまでもJavaサンプルを基に書いたイメージの実装なので色々と間違っていたらすみません。）

[code]

    export abstract class Factory {
      static getFactory(className: string): Factory {
        const factory: Factory = /* new 'className'() のようなことがしたい */;
        return factory;
      }
      // do something
    }
    
    export class ListFactory extends Factory {
      // do something
    }
    
    export class TableFactory extends Factory {
      // do something
    }
    
    // コマンドラインから'ListFactory'などの文字列を受け取り動的に処理を進めたい
    const className: string = process.argv[2];
    const f = Factory.getFactory(className);
    // do something
    
[/code]


CC BY-SA 4.0
2020-05-01T15:12:01.587
66185
2020-05-02T23:34:07.317
2020-05-02T07:07:58.840
39932
39932
question
javascript node.js typescript
TypeScript (Node.js) で文字列から動的にインスタンスを作成する方法


(Rubyについては知らないのでJavaとJavaScript/TypeScriptのみについて言及しています)

Javaで `Class.forname("クラス名文字列")`
呼び出しを行って`Class<?>`クラスを取得できるのは、クラスローダが`Class<?>`クラスの管理を行っているおかげです。

JavaScriptは[プロトタイプベース](https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%97%E3%83%99%E3%83%BC%E3%82%B9)なのでそのようなクラス管理の機構はありません。  
したがってクラス名を指定したらそれに対応するオブジェクトが返ってくるような仕組みは自前で作る必要があります。

  * 初期処理としてレジストリにファクトリ具象クラスを登録する
  * 名前でレジストリを検索して対応するファクトリクラスを `new` して返す

* * *

次のリンク先のコードがほぼ今回の要求に答えるものになっているかと思います(呼び出し元で `new`しているのが今回の要求と異なりますが):

  * [Typescript - Get all implementations of interface - Stack Overflow](https://stackoverflow.com/a/47082428/4506703)

このコードは[class
decorators](https://www.typescriptlang.org/docs/handbook/decorators.html#class-
decorators)を利用してレジストリへのファクトリクラス登録を意識させない作りになっていますが、もうちょっと _もっさりした_
書き方をすると次のような感じになります:

[code]

    // 大筋は次を参考にした: https://stackoverflow.com/a/47082428/4506703
    
    // Angular styleのクラス型定義: https://stackoverflow.com/a/52183279/4506703
    interface Constructable<T> extends Function {
      new (...args: any[]): T;
    }
    
    export abstract class Factory {
      static factories: Constructable<Factory>[] = [];
      static getFactory(className: string): Factory | undefined {
        const factory = this.factories.find((e) => e.name === className);
        if (factory) {
          return new factory();
        } else {
          return undefined;
        }
      }
      // do something
    }
    
    export class ListFactory extends Factory {
      // do something
    }
    
    export class TableFactory extends Factory {
      // do something
    }
    
    // ファクトリ具象クラスの登録
    // Javaのクラスローダのようなものはないので自前で管理する
    Factory.factories.push(ListFactory, TableFactory);
    
    // コマンドラインから'ListFactory'などの文字列を受け取り動的に処理を進めたい
    const className: string = process.argv[2];
    const f = Factory.getFactory(className);
    // do something
    
[/code]

(質問文中コードに合わせた書き方をしましたが、`Factory`レジストリ機能は `Factory`クラスから分離したほうが自然だと思います)


CC BY-SA 4.0
2020-05-02T23:34:07.317
66221
2020-05-02T23:34:07.317
2808
66185
answer