TypeScript1.8以降で追加予定または協議中の注目の新機能を紹介します。
この記事はTypeScript アドベントカレンダー2015 25日目の記事です。
戻り値型の厳格な検査(--noImplicitReturns) [Merged]
戻り値が定義した型以外になることを禁止します。 必ずオンにしましょう。しない理由がありません。 この調子で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]
nullやvoidがanyやvoid以外の型に混入しないよう暗黙的なキャストを禁止します。
変数ならconstを使う方法もあるのですがオブジェクトやパラメーターはそうはいきません。
--noImplicitNullオプションでの制御を支持しています。
バグのない世界のために今すぐ+1!
let n: number = null; // error let m: number = undefined; // error
タイプガードへのコントロールフロー解析の適用 [Discussion]
タイプガードはこれまで条件分岐先のブロックか式にしか適用されませんでしたがタイプガードの結果が以降のすべてのコードに適用されるようになるのでガード節を型解決に使えるようになります。 まだ協議中なのでぜひ+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]
変数や関数に与えられる文字列の値を制限できるようになります。 ありがてぇ。
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]
プロパティの再代入を型として禁止し、不変オブジェクトの定義を可能にします。
待ち望まれた機能です。
個人的には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]
今まで地味にオブジェクト型で制約する型がありませんでしたがこれでようやく制約できるようになります。
出力したJSモジュールファイルの結合 [Merged]
出力したJSモジュールファイルを1つのファイルに結合します。
Browserifyがいらなくなくなります。 単にオプションを使って結合しただけではrequirejsに依存しますが、以下のスニペットをヘッダとして追加することでブラウザでもNodeでも動作するIsomorphicなライブラリとなります。
こちらのライブラリで実際にこの機能を使ってBrowserifyなしで同じ相互運用性を実現していますので試してみたい方は参考にするとよいでしょう。
循環参照が許されませんが、なんくるないさという気持ちになれば問題ありません。
ファイルパスのglobによるパターンマッチ [Scheduled]
ファイルパスにワイルドカードなどのglobのパターンマッチを使えるようにします。 コンパイルするファイルを何十個も列挙してメンテまでするのはつらすぎます。 v2.xで実装が予定されています。
Union/Intersection typesの型推論強化 [Merged]
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]
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]
型パラメーターで型パラメーターの制約を作れるようになります。 今まで何度この型を書けずに涙を飲んだかわかりません。
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]
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]
マイクロソフトによると指がすべって立てられたIssueはデザインによるものだそうです。
草。
まとめ
TypeScriptはバージョンアップを重ねるたびにますます洗練されていき、来年は上述の機能の追加によりさらに躍進が期待されます。
2016年もさらにヒートアップするTypeScriptをよろしく。