yaakaito.org

TypeScriptのGenerics試してみる

TypeScript

こんにちは!うきょーです!TypeScript0.9alpha使える環境作ったのでジェネリクス試すよ! typescript-dddbaseというのをはじめていたので、これを0.9対応することを見越して、Identity Entity Repositoryをジェネリクス使って考えてみる。

使い方

classinterfaceで名前与えられたものと、string number なんかを使う事が出来ます。 シンタックスはよくある感じで、特に違和感もない。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Identity<T> {
    value: T;
}

class NumberIdentity implements Identity<number> {
    constructor(public value: number) { }
}

class StringIdentity implements Identity<string> {
    constructor(public value: string) { }
}

var numberIdentity = new NumberIdentity(1);
var stringIdentity = new StringIdentity("A");

クラスのときはこんな感じ。

1
2
3
class Entity<T extends Identity> {
    constructor(public identity: T) { }
}

推論

上のEntityみたいなやつの場合、コンストラクタで推論してくれる。 さっき作ったnumberIdentityを使ってみる。

1
var entity = new Entity(numberIdentity);

これのidentitystringIdentityで上書きしようとすると、

1
2
var entity = new Entity(numberIdentity);
entity.identity = stringIdentity;

こうなる

1
2
error TS2012: Cannot convert 'StringIdentity' to 'NumberIdentity':
  Types of property 'value' of types 'StringIdentity' and 'NumberIdentity' are incompatible.

やりましたね!

もちろんですが、こういう風にも書けます。

1
var entity = new Entity<NumberIdentity>(numberIdentity);

推論できない場合には明示的に書く必要があります。

ネスト

ネストも出来ます。Repositoryを考えたときに、これはEntity<T>の集合なので、

1
2
class Repository<E extends Entity<I>, I extends Identity> {
}

こう書けます。 本体もとりあえず書いてみる。(めんどくさいし説明用なので、ただvalueにいれて返すだけにした)

1
2
3
4
5
6
7
8
9
10
11
12
class Repository<E extends Entity<I>, I extends Identity> {

    public value: E = null;

    store(entity: E) {
        this.value = entity;
    }

    findByIdentity(identity: I): E {
        return this.value;
    }
}

これの場合は推論できないので、このクラスを直接使う場合は、

1
2
var repository = new Repository<Entity<NumberIdentity>, NumberIdentity>();
repository.store(entity);

こんな感じになります。

もうちょっと書いてみる

ここまでのを元にPersonモデルとPersonRepositoryを作ってみる、実装はない。

1
2
3
4
5
6
7
8
9
10
11
12
class Person extends Entity<NumberIdentity> {
    constructor(public identity: NumberIdentity) {
        super(identity);
    }
}

class PersonRepository extends Repository<Person, NumberIdentity> {
}

var person = new Person(numberIdentity);
var personRepository = new PersonRepository();
repository.store(person);

こんな感じ。

関数

引数の前に書く。

1
<T>(x: T) => x

みたいな

ところで・・・

試してて途中で気づいたんだけど、

1
2
3
4
5
6
7
8
class Hoge {
    public a: string;
}
class Fuga {
    public a: string;
}

var hoge: Hoge = new Fuga();

これが通るようになってた。で、2時間くらいハマってた。空のクラス定義にするとArray<Hoge>array.push("ばーか")とかできるよ、やりましたね。 試したのはrelease-0.9.0-alphadevelop@c3835abで、どっちも通った。

これって0.8.xだとCannot convert 'Fuga' to 'Hoge'とかで通らなくて、やりたいならInterface通してね、 みたいな感じになってたと思うんだけど、変更あったんだろうか? いくらか探してみたんだけど、それっぽいものを見つけられなくて、よくわかっていない。 眠いし見落としてる可能性もあるので、知ってる人いたら教えてほしい。 挙動は分からんでもないし、バグかなーと思ったんだけど、さすがにテストでこけるんじゃぁ・・・的な。ツッコミなければ投げるつもり。