2017-08-15 7 views
0

現在、レガシーコードをプロジェクトのタイプスクリプトに変換しています。このプロジェクトには、純粋なjavascriptで書かれた多数のカスタムクラスが含まれています。カスタムクラスは、その中に定義された多くの設定オプションを持つconfigオブジェクトを取り込むコンストラクタを持つ同じパターンに従います。オブジェクトの分解を使用してクラスメンバーを割り当てる

私はtypescriptで許可された設定のプロパティを定義する方法を試してきましたが、まだ私が完全に満足している解決策を思いついていません。私はconfigのインターフェースを宣言することから始めました。これは設定オプションを定義することを可能にしますが、デフォルト設定には役立ちません。定義と初期化の両方に対して設定インターフェースを2回以上コピーする必要があります。私は(Iと思う)したいことは、デフォルト値で一度クラスのメンバを定義するには、オブジェクトdestrcuturingを使用し、その例の後に、それを心配する必要がないことです。

:その後のようなもので初期化することができ

export interface MyClassConfig { 
    propA: string; 
    propB: boolean; 
} 

export class MyClass { 
    private propA: string = 'defautValue'; 
    private propB: boolean; 

    constructor (config: MyClassConfig) { 
     { propA, propB } = config; 
    } 
} 

let mc = new MyClass({ propB: false }); //mc.propA == 'defaultValue', mc.propB === false 

これは私がプロパティ名を何度もコピーする必要があるのは嫌いです。誰もこれを達成するための素晴らしいクリーンな方法を提案することはできますか?

+0

ため

参照JSBinに? – gilamran

+0

@gilamran私の主な動機は、設定オプションをクラス外から変更できないようにすることです。私が設定オブジェクトを参照すると、作成者は設定オブジェクトを参照することによって設定オプションを変更することができます。 'let config = {myProp: '何か'};新しいMyClass(config); config.myProp = 'somethingElse'; '私はこれを望んでいません。 – Samih

答えて

1

configオブジェクトをクラスにメンバオブジェクトとして追加するという大きな問題はありません。最新のTypescriptバージョンの助けを借りて、現在のすべてのソリューションは素晴らしくなく、ちょうどこれ以上簡単なものが複雑になります。同様のものがCombining destructuring with parameter propertiesより前に提案されています。この提案はほぼ2歳です。しかし、上記を行うには数多くの方法がありますが、クラス内のフィールドにアクセスする必要があるかどうかはわかりません。あなたは次のインターフェイス与えられ、上記を解決するのに役立つかもしれないいくつかの方法で上へ、

...

interface IConfigOpts { 
    propA: string, 
    propB: boolean 
    propC: number 
} 

...と、このクラス構造。

class Config { 

    constructor(opts: IConfigOpts) { 
     // Object.assign(this, opts); 
     Object.entries(opts).forEach((property: [string, any]) => { 
      Object.defineProperty(this, property[0], { 
       writable: true, 
       value: property[1] 
      }); 
     }); 
    } 

    get props() { 
     return Object.getOwnPropertyNames(this).map(key => ({[key]: this[key]})); 
    } 
} 

あなたは、コンストラクタでメンバフィールドを初期化するためにフランクによって提案されObject.assign(ES5 +)、またはObject.definePropertyを使用することができます。後者は、プロパティをwritableにするか、gettersおよび/またはsettersを割り当てるオプションを与えます。

あなたのConfigクラスやインタフェースの新しいインスタンスの作成を使用して、クラスの外で自分のタイプのプロパティを取得するためにファクトリメソッドを作成することができます。

const ConfigFactory = (opts: IConfigOpts) => new Config(opts) as Config & IConfigOpts; 

const cfg = ConfigFactory({ 
    propA: 'propA', 
    propB: true, 
    propC: 5 
}); 

let a: string = cfg.propA; // OK => defined 

しかし、これはあなたを許しません型チェックのあるクラスでthis.propAを使用します。 (まだ)は、型付けされたクラスメンバーを、破壊を使用してクラスに動的にバインドします。代わりにthis[key]を使用する必要があります。

クラス外のプロパティにアクセスしたくない場合は、名前でプロパティを呼び出すメソッドを定義し、その型を使用して指定されたインターフェイスで定義された型を検証します。this[key]それらの指定された型を返す:

prop<TKey extends keyof IConfigOpts, TValue extends IConfigOpts[TKey]>(key: TKey): TValue { 
    return this[key as string]; 
} 

これは、入力されたキーはkeyofでルックアップを使用してインターフェイスにに存在する保証と同様に正しいタイプを返します。

let a: string = cfg.prop('propA'); // OK => defined 
let b: boolean = cfg.prop('propB'); // OK => defined 
let c: number = cfg.prop('propC'); // OK => defined 

let d: number = cfg.prop('propD'); // Fail => Argument is not assignable to keyof IConfigOpts 
let e: boolean = cfg.prop('propC'); // Fail => Type number is not assignle to type boolean 

自動的にクラスにプロパティ(の多く)を結合についての唯一の良いところは、あなたがオブジェクトのキーを一致させるために与えられたコンストラクタでオブジェクトのdestructureを編集する必要がないことです。例えばpropApropABに変更されます。変更する必要があるのは、インタフェース宣言だけです。

1

コンストラクタでthisの参照プロパティに構造体を使用することはできません。 Object.assignを使用して、設定からプロパティにプロパティをコピーするのはどうですか?

constructor (config: MyClassConfig) { 
    Object.assign(this, config); 
} 

これは、参照型の浅いコピーを作成することに注意してください。 MyClassのプロパティ名のいずれかが変更された場合は、プロパティ名をMyClassConfigに変更することを忘れないでください。

+0

ありがとうございます - 私はECMAScript 6の質問でこの解決策を見ていましたが、タイスクリプトソリューションがあるかどうか疑問に思っていました。私もIE11をサポートしなければならないので、polyfillが不要になることを望んでいました。 – Samih

0

私は同じ問題に直面していました。クラスメンバーを分解して割り当てます。

interface MyClassConfig { 
    propA?: string; // Note the '?' for optional property 
    propB: boolean; 
} 

class MyClass { 
    private propA: string = 'defautValue'; 
    private propB: boolean; 

    constructor (config: MyClassConfig) { 
     ({ propA: this.propA = this.propA, propB: this.propB } = config); 
    } 
} 

const myClass = new MyClass({ propA: 'aaa', propB: true }); 
console.log(myClass) 

// MyClass: 
// propA = 'aaa'; 
// propB = true; 

const myClass2 = new MyClass({ propB: false }); 
console.log(myClass2) 

// MyClass: 
// propA = 'defaultValue'; 
// propB = false; 

は、「デフォルト値を新しい変数に名前を割り当てと提供する」MDN Destructuring assignmentの章を参照してください:

私は以下の私のニーズを満たすソリューションと、おそらくあなたを見つけました。だけではなく、プロパティにそれを破るの設定オブジェクトを保存しないのはなぜworking sample

関連する問題