TypeScript の仕様を眺めた

TypeScript、JavaScript とある程度互換性あるし、読みやすい JavaScript のコードに変換してくれるし、既に実験場仕様書もあってすごい。

ということでちょっと仕様書をざっと眺めてみました。そのときに面白いなーとか思ったのとかをメモ。

Structual Subtyping

Structual Subtyping は、静的型付けの型チェックと、動的型付けのアドホックを混ぜあわせたような、いいとこ取りの機能で、

interface Point {
    x: number;
    y: number;
}

function getX(p: Point) {
    return p.x;
} 

というのがあるとき、

class CPoint {
    constructor (public x: number, public y: number) { }
}

getX(new CPoint(0, 0));  // OK、p.x, p.y とアクセスすることができる
getX({ x: 0, y: 0, color: "red" });  // OK、余分なフィールドがあっても同様にアクセスできる
getX({ x: 0 });  // エラー、p.y とアクセスできない

こんな風に、

  • とにかく p.x, p.y とアクセスできるなら何でも渡すことができる(動的型付けっぽさ)
  • p.x, p.y とアクセスできないならコンパイルエラー(静的型付けっぽさ)

という感じになります。


これが実際どんなケースで嬉しいかというと、例えば jQuery
jQuery は既に JavaScript のライブラリとして存在しているわけですが、この jQuery に対して一切変更することなく「後付けで」インターフェースを定義して、型チェックの恩恵に与れます。 (この後付け jQuery インターフェースは jquery.d.ts にあるようです)



ちなみに既存のオブジェクトに対して後で型を付ける場合、

declare var $: JQueryStatic;

とかしてやればいいようです。

型推論

引数は推論されませんが、戻り値は推論されるようです。

function f(x) {
    return 10;
}

と書いた場合、f 関数の型は (x: any) => number です。

function f(x) {
    if (x == 0) return 10;
    else return "hoge";
}

と書いた場合、型がわからないということで、コンパイルエラーになりました(これは JavaScript と互換性のない動作)。

function f(x): any {
    if (x == 0) return 10;
    else return "hoge";
}

と書けばコンパイルは通ります(x は変わらず any 型のまま)。

アロー関数式

要するにラムダ式

var f = x => x+1; // (x:any) => any

しかし残念なことに、普通の関数と同じく、x は any 型です。
x を使っているので、戻り値も any 型です。(any型 + number型はany型になる at 4.15.2)


マジメに型を付けるなら

var f = (x:number) => x+1; // (x:number) => number

と書きます。

マップ型

マップは、それぞれ別々の型になるようです。

function f(v:{ a:number; b:string; }) {
  return { x:10, y:20 }; // 戻り値は { x:number; y:number; } 型
}

配列型

var v: number[];

でいけます。2次元配列なら

var v: number[][];

です。

多相型

残念なことに自作はできないようです。

Type Assertion

要するにキャスト。
これは例えば、こういう風に使います。

var canvas = <HTMLCanvasElement>document.createElement("canvas");

document.createElement が返す型は HTMLElement なのですが、それを HTMLCanvasElement として扱うようにします。


あとは、

interface Point {
  x: number;
  y: number;
}
var p:Point = { x:10, y:<number>undefined };

とかすれば、型チェックは通るようになります(実際に動作するかどうかはともかく)。

オーバーロード

関数は、型が違ってたりパラメータの数が違ってたりするとオーバーロードできます。
…はずなんだけど、実験場ではコンパイルエラーになりました。

クラス

public, private 指定、static 指定、継承、super を使った親クラス呼び出し、コンストラクタでメンバの定義と初期化など、普通の機能が揃ってます。

モジュール

誰か解説お願いします!