TypeScript1.8以降で追加予定または協議中の注目の新機能12つ

TypeScript1.8以降で追加予定または協議中の注目の新機能を紹介します。

この記事はTypeScript アドベントカレンダー2015 25日目の記事です。

戻り値型の厳格な検査(--noImplicitReturns) [Merged]

github.com

github.com

戻り値が定義した型以外になることを禁止します。 必ずオンにしましょう。しない理由がありません。 この調子でFlowtypeと型安全性をガンガン競ってほしいところです。

// The followings are all wrong in a typed context yet the compiler ignores them as a valid code:
function SomeMethod(): number {
    return;
}

function SomeMethod(): number {
    return null;
}

function SomeMethod(): number {
    if (false)
        return 1;
}

function SomeMethod(): number {
    try {
        return 0;
    }
    catch (ex) {
    }
}

Nullable型の禁止(--noImplicitNull) [Suggestion]

github.com

nullやvoidがanyやvoid以外の型に混入しないよう暗黙的なキャストを禁止します。 変数ならconstを使う方法もあるのですがオブジェクトやパラメーターはそうはいきません。 --noImplicitNullオプションでの制御を支持しています。 バグのない世界のために今すぐ+1

let n: number = null; // error
let m: number = undefined; // error

タイプガードへのコントロールフロー解析の適用 [Discussion]

github.com

タイプガードはこれまで条件分岐先のブロックか式にしか適用されませんでしたがタイプガードの結果が以降のすべてのコードに適用されるようになるのでガード節を型解決に使えるようになります。 まだ協議中なのでぜひ+1してきてください。

function foo(x: number | string) {
    if (typeof x === 'string') {
        return;
    }
    // x should now be a number
    if (2 % x !== 0) {
        // do something
    }
}

String Literal Types [Merged]

github.com

変数や関数に与えられる文字列の値を制限できるようになります。 ありがてぇ。

type CardinalDirection = "North"
                       | "East"
                       | "South"
                       | "West";

function move(distance: number, direction: CardinalDirection) {
    // ...
}

let a: "foo" | number = "foo"; // valid
let b: "bar" | "baz" = "foo";  // invalid

Readonly(final)アクセス修飾子 [Scheduled]

github.com

プロパティの再代入を型として禁止し、不変オブジェクトの定義を可能にします。 待ち望まれた機能です。 個人的にはfinalのほうが好きです。

interface Point {
    x: number;
    y: number;
}
interface ImmutablePoint {
    readonly x: number;
    readonly y: number;
}
var pt: ImmutablePoint = { x: 4, y: 5 }; // OK, can convert mutable to non-mutable
pt.x = 5; // Error, 'pt.x' is not a valid target of assignment

var pt2: Point = pt; // Error, cannot convert readonly 'x' to mutable 'x'

// Possibly bad behavior
var pt3: Point = { x: 1, y: 1 };
var pt4: ImmutablePoint = pt3; // OK
pt3.x = 5; // pt4.x is also changed?

// Really bad behavior
/** This function was written in TypeScript 1.0 **/
function magnitudeSquared(v: { x: number; y: number }) {
   return v.x * v.x + v.y * v.y;
}
// Now try to use it with ImmutablePoint
console.log(magnitudeSquared(pt)); // Error, cannot use readonly object in non-readonly call

object型の追加 [Accepted]

github.com

今まで地味にオブジェクト型で制約する型がありませんでしたがこれでようやく制約できるようになります。

出力したJSモジュールファイルの結合 [Merged]

github.com

出力したJSモジュールファイルを1つのファイルに結合します。

Browserifyがいらなくなくなります。 単にオプションを使って結合しただけではrequirejsに依存しますが、以下のスニペットをヘッダとして追加することでブラウザでもNodeでも動作するIsomorphicなライブラリとなります。

gist.github.com

こちらのライブラリで実際にこの機能を使ってBrowserifyなしで同じ相互運用性を実現していますので試してみたい方は参考にするとよいでしょう。

github.com

循環参照が許されませんが、なんくるないさという気持ちになれば問題ありません。

ファイルパスのglobによるパターンマッチ [Scheduled]

github.com

ファイルパスにワイルドカードなどのglobのパターンマッチを使えるようにします。 コンパイルするファイルを何十個も列挙してメンテまでするのはつらすぎます。 v2.xで実装が予定されています。

Union/Intersection typesの型推論強化 [Merged]

github.com

Union/Intersection typeの分解時の型推論が強化され再利用性が向上します。 とてもうれしい。

type Maybe<T> = T | void;

function isDefined<T>(x: Maybe<T>): x is T {
    return x !== undefined && x !== null;
}

function isUndefined<T>(x: Maybe<T>): x is void {
    return x === undefined || x === null;
}

function getOrElse<T>(x: Maybe<T>, defaultValue: T): T {
    return isDefined(x) ? x : defaultValue;
}

function test1(x: Maybe<string>) {
    let x1 = getOrElse(x, "Undefined");         // string
    let x2 = isDefined(x) ? x : "Undefined";    // string
    let x3 = isUndefined(x) ? "Undefined" : x;  // string
}

function test2(x: Maybe<number>) {
    let x1 = getOrElse(x, -1);         // number
    let x2 = isDefined(x) ? x : -1;    // number
    let x3 = isUndefined(x) ? -1 : x;  // number
}

thisの型定義と型推論 [Discussion]

github.com

thisの型定義は避けては通れないのではないでしょうか。 クラスの自己型としてのthisはTS1.7のポリモーフィックthisで実装済みです。

let f = function(this: {data: number}) {
  console.log(this.data2) 
}
let o = {
  data: 12
  f: f
  g: function() {   // this is inferred from the contextual type
    console.log(this.data); 
  }
}
function f(this: {n: number}, m: number) {
    return this.n + m;
}
class C {
  n: number
  m1(this:this, m: number) {
    return this.n + m
  }
  m2(this: {n: number}, m: number) {
    return this.n + m
  }
}

型パラメーターによる制約 [Merged]

github.com

型パラメーターで型パラメーターの制約を作れるようになります。 今まで何度この型を書けずに涙を飲んだかわかりません。

function assign<T extends U, U>(target: T, source: U): T {
    for (let id in source) {
        target[id] = source[id];
    }
    return target;
}

let x = { a: 1, b: 2, c: 3, d: 4 };
assign(x, { b: 10, d: 20 });
assign(x, { e: 0 });  // Error

?プロパティーアクセサ [Suggestion]

github.com

CoffeeScriptの便利なやつです。 ESの仕様に入ってほしいくらいです。 ただ最初に提案されたものはx?.y?.zの戻り値の型がx|y|zであまりよろしくありません。

var x = { y: { z: null, q: undefined } };
console.log(x?.y?.z?.foo); // Should print 'null'
console.log(x?.baz); // Still an error
console.log(x.y.q?.bar); // Should print 'undefined'

ミスタイプ(ポスト)はデザイン [Design]

github.com

マイクロソフトによると指がすべって立てられたIssueはデザインによるものだそうです。

草。

まとめ

TypeScriptはバージョンアップを重ねるたびにますます洗練されていき、来年は上述の機能の追加によりさらに躍進が期待されます。

2016年もさらにヒートアップするTypeScriptをよろしく。