2013-03-08 4 views
11

Backbone.jsでTypeScriptを使用しようとしています。それは "動作"しますが、タイプの安全性の大半はBackboneのget()とset()によって失われます。私は型安全性を復元するヘルパーメソッドを記述しようとしています。このような何か:バックボーンとタイプスクリプト、不幸な結婚式:タイプセーフな「get」の構築?

私は私のモデルでこれを置くところ:

消費者で
object() : IMyModel { 
    return attributes; // except I should use get(), not attributes, per documentation 
} 

そして、この: var myVar = this.model.object().MyProperty;

この構文では、私はMYPROPERTYが存在し、ブール値である活字体の知識を得ますそれは素晴らしいです。しかし、backbone.js docsは、属性ハッシュではなくgetとsetを直接使用するように指示します。それでは、getとsetを使ってオブジェクトのパイプ使用を魔法のJavascriptで処理する方法はありますか?

+0

答えはありませんでしたが、私は同じ問題を発見し、データとバインディングを管理するためのツールの別のセットを使いました(プレーンだが強く型付けされたTSオブジェクト+ Linq。 js + jsrender/jsviews(その観測可能なクラス - http://bit.ly/WyNbhn))この非常に理由のために。参考文献:http://linqjs.codeplex.com/ https://github.com/BorisMoore – JcFx

+0

TypeScriptがジェネリックをサポートするのを待つ必要があるかもしれないと思います。9) –

+0

@RyanCavanaugh現在の答えは受け入れられず、配列のようなもっと複雑なオブジェクトやジェネリックのためのサポートを持つTypeScriptで崩れているかもしれません。 – parliament

答えて

16

私たちは、TypeScriptの大きなバックボーンを使用しており、斬新な解決策を考え出しています。インターフェースは、モデルのプロパティを定義し、コンストラクタは、渡された任意のオブジェクトは、ID、名前と説明の特性を有することを保証すること

interface IListItem { 
    Id: number; 
    Name: string; 
    Description: string; 
} 

class ListItem extends Backbone.Model implements IListItem { 
    get Id(): number { 
     return this.get('Id'); 
    } 
    set Id(value: number) { 
     this.set('Id', value); 
    } 
    set Name(value: string) { 
     this.set('Name', value); 
    } 
    get Name(): string { 
     return this.get('Name'); 
    } 
    set Description(value: string) { 
     this.set('Description', value); 
    } 
    get Description(): string { 
     return this.get('Description'); 
    } 

    constructor(input: IListItem) { 
     super(); 
     for (var key in input) { 
      if (key) { 
       //this.set(key, input[key]); 
       this[key] = input[key]; 
      } 
     } 
    } 
} 


次のコードを考えます。 for文は各プロパティのバックボーンセットを単に呼び出すだけです。このような次の試験に合格すること:

describe("SampleApp : tests : models : ListItem_tests.ts ",() => { 
    it("can construct a ListItem model",() => { 
     var listItem = new ListItem(
      { 
       Id: 1, 
       Name: "TestName", 
       Description: "TestDescription" 
      }); 
     expect(listItem.get("Id")).toEqual(1); 
     expect(listItem.get("Name")).toEqual("TestName"); 
     expect(listItem.get("Description")).toEqual("TestDescription"); 

     expect(listItem.Id).toEqual(1); 

     listItem.Id = 5; 
     expect(listItem.get("Id")).toEqual(5); 

     listItem.set("Id", 20); 
     expect(listItem.Id).toEqual(20); 
    }); 
}); 

アップデート:私はES5を取得し、構文だけでなく、コンストラクタを設定し、使用するコードベースを更新しました 。基本的には、バックボーンの.getと.setを内部変数として使用できます。

+1

listItem.Name == 'TestName'ですか?そうでない場合、その小道具はなぜ定義されていますか? listItem.set( "Name"、 "NewName")を使用すると、両方の場所に反映する必要があります... –

+0

OK、ES6のgetterとsetterの構文を使用するようにコードを変更しました。これがあなたのために働くかどうか私に教えてください。お楽しみください – blorkfish

+0

このトピックは、強く型付けされたバックボーンモデルのクイックブログを投稿してくれました。http://blorkfish.wordpress.com/2013/03/20/typescript-strongly-typed-backbone-models/ – blorkfish

9

私は、/u/blorkfish答えを捨てて、ジェネリックスとES5ゲッター/セッターを使って次のことを考え出しました。

class TypedModel<t> extends Backbone.Model { 
    constructor(attributes?: t, options?: any) { 
     super(attributes, options); 

     var defaults = this.defaults(); 
     for (var key in defaults) { 
      var value = defaults[key]; 

      ((k: any) => { 
       Object.defineProperty(this, k, { 
        get:(): typeof value => { 
         return this.get(k); 
        }, 
        set: (value: any) => { 
         this.set(k, value); 
        }, 
        enumerable: true, 
        configurable: true 
       }); 
      })(key); 
     } 
    } 

    public defaults(): t { 
     throw new Error('You must implement this'); 
     return <t>{}; 
    } 
} 

注:Backbone.Modelデフォルトはオプションですが、我々はゲッターとセッターを構築するためにそれを使用するので、それが今では必須です。スローされたエラーによって、これが強制的に実行されます。多分もっと良い方法を考えることができますか?

そして、それを使用する:

interface IFoo { 
    name: string; 
    bar?: number; 
} 

class FooModel extends TypedModel<IFoo> implements IFoo { 
    public name: string; 
    public bar: number; 

    public defaults(): IFoo { 
     return { 
      name: null, 
      bar: null 
     }; 
    } 
} 

var m = new FooModel(); 
m.name = 'Chris'; 
m.get('name'); // Chris 
m.set({name: 'Ben', bar: 12}); 
m.bar; // 12 
m.name; // Ben 

var m2 = new FooModel({name: 'Calvin'}); 
m2.name; // Calvin 

それは少し冗長な理想的だし、それはデフォルトを使用する必要がありますが、それはうまく動作します。ここで

+0

+1私はこれを試していないが、うまくいくように見える。しかし、どのように配列を扱うのでしょうか?たとえば、 'm.SomeArray.push(item)'は、これがBackboneイベントをトリガーしますか?あなたがモデルを取得し、アイテムをプッシュし、モデルを設定しなければならない、それはかなり醜いネイティブのバックボーンの方法。 ( 'var Array = m.get(" SomeArray "); array.push(item); m.set(" SomeArray "、array);)。あなたの実装はこれをより自然にすることができますか? – parliament

+0

あなたのセットは基本的に何もしていないので、あなたのサンプルメソッドは動作しません。 array === m.get( 'SomeArray')それらはオブジェクト参照が同じであるためです。サイレントunsetしてからsetを実行するか、配列を複製して新しい配列に値を設定するか、または手動で変更イベントをトリガーする必要がありますか? –

+0

どちらの方法でも、私の実装はバックボーンのgetとsetの呼び出しを呼び出すので、あなたがしなければならない回避策は同じです。 –

0

はこのような基本クラスを作成し、デコレータを使用しての方法です:

export class Model<TProps extends {}> extends Backbone.Model { 

    static Property(fieldName: string) { 
     return (target, member, descriptor) => { 
      descriptor.get = function() { 
       return this.get(fieldName); 
      }; 
      descriptor.set = function(value) { 
       this.set(fieldName, value); 
      }; 
     }; 
    } 

    attributes: TProps; 
} 

次に、このような独自のクラスを作成:

class User extends Model<{id: string, email: string}> { 
    @Model.Property('id')  set Id(): string { return null; } 
    @Model.Property('email')  set Email(): string { return null; } 
} 

をし、それを使用します。

var user = new User; 
user.Email = '[email protected]'; 
console.log(user.Email); 
関連する問題