2017-05-18 23 views
1

ジェネリック関数を使用してクラスに対して静的メソッドを作成するにはどうすればよいですか?Generic関数を使用してTypeScriptクラスを動的に変更する

静的shouldMakeがtrueまたはfalseであるかどうかに応じて、一連のクラスに関数makeを追加したいとします。これらのmakeファンクションは、ファクトリであり、クラスのインスタンスを作成する必要があります。

次のJavaScriptは動作します:

function makeMaker(cls) 
{ 
    if (cls.shouldMake) 
     cls.make = function(...args) { return new cls(...args); }; 
    return cls; 
} 

あなたはこのを通じてクラスのセットを実行することができます。

outClasses = inClasses.map(makeMaker); 

私は活字体で動作するように、このような何かを期待する:

function makeMaker<T>(cls: T): T 
{ 
    if (cls.shouldMake) 
     cls.make = function(...args: any[]) { return new cls(...args); } 
    return cls; 
} 

ただし、これによりエラーが発生します:

プロパティ 'shouldMake'がタイプ 'T'に存在しません。

プロパティ 'make' を型 'T' に存在しません

は、そのタイプのコールまたは構築署名を欠いた表現で「新しい」を使用することはできません。この場合

clsは、型Tの値を推測するであろう。あなたが引数(cls)としてクラスを「クラスの型」のようなものにすると、Tを期待できます。コンストラクタコンストラクタにそれを変更する:

function makeMaker<T>(cls: { new(...args: any[]): T }) 
{ 
    if (cls.shouldMake) 
     cls.make = function(...args: any[]) { return new cls(...args); }; 
    return cls; 
} 

原因:

プロパティ 'shouldMakeは、' タイプ ':=> T新しい(任意の[] ...引数)' に存在しません。

プロパティ 'make' を型 '新しい(... argsを:任意の[])=> T' に存在しません。

これは意味がありますが、makeMakerが呼び出されない場合でもこのエラーが発生します。したがって、Tのコンストラクタ(Tクラス自体)がshouldMakemakeプロパティが有効な型であることを保証する必要があります。

どうすればよいですか?基本的には、どのようにクラスをタイプにするかは、これを制限できるインターフェイスから継承するか、有効なstaticプロパティをクラスでどのように記述しますか?これは、https://github.com/Microsoft/TypeScript/issues/13462https://github.com/Microsoft/TypeScript/issues/14600に応じてサポートされていないようです。このロジックに応じてJavaScriptコードベースが大きくなると、TypeScriptに変換できなくなります(多少の完全な書き換えは除外されます)。

答えて

1

これは、の使用方法のように聞こえるかもしれません。とタイプキャストの可能性があります。実際の解決策に着く前に、必要なプリアンブルについて説明します。まず、我々は(タイプTのオブジェクトのコンストラクタで)メーカーになることができ、すべてのクラスタイプのために実装されるインタフェース宣言することができます(

interface Class<T> { 
    shouldMake: boolean; 
    new(... args: any[]): T; 
} 

はその後、我々はメーカーすることができ、すべてのオブジェクトのためのインタフェースを定義し、それらタイプTのオブジェクトを作成します)。メソッドが利用可能かどうかは、フィールドshouldMakeによって異なります。

interface Maker<T> { 
    shouldMake: boolean; 
    make?(...args: any[]): T; 
} 

makeMakerを実装できます。クラスオブジェクトをC & Maker<T>にキャストすると、makeが存在する可能性があり、通常通り設定できます。メソッドのプロトタイプの重要な部分は、CにコンストラクタとshouldMakeフィールドがあり、返される型がCに関するすべての情報と、余分なメソッドをMakerに保持していることを確認することです。

function makeMaker<T, C extends Class<T>>(cls: C): C & Maker<T> 
{ 
    if (cls.shouldMake) 
     (<C & Maker<T>>cls).make = function(...args: any[]) { return new cls(...args); }; 
    return cls; 
} 

たJavaScriptは、あなたが最初に持っていたものと同等です:

function makeMaker(cls) { 
    if (cls.shouldMake) 
     cls.make = function (...args) { return new cls(...args); }; 
    return cls; 
} 

ここでの使用例です。コンパイラはあなたのためにそれを確認することはできませんので、常にmakeが利用可能かどうかを確認する必要があります。

class Foo { 
    private name?: string; 

    static shouldMake = true; 

    new(name?: string) { 
     this.name = name; 
    } 

    hasName(): boolean { 
     return typeof this.name === 'string' && this.name !== ''; 
    } 
} 

const FooMaker = makeMaker<Foo, typeof Foo>(Foo); 
if (FooMaker.shouldMake) { 
    let unnamedFoo = FooMaker.make(); 
    console.log(unnamedFoo.hasName()); // false 
} else { 
    // unreachable in this case 
} 
関連する問題