TypeScript 2.0

nullとundefinedを認識する型

TypeScriptにはヌル(Null)と未定義(Undefined)という2つの特別な型があり、 それぞれnullundefinedの値を持ちます。

以前はこれらの型を明示的に指定することはできませんでしたが、 型検査のモードに関係なく、型名としてnullundefinedを使用できるようになりました。

以前は、型チェッカーはnullundefinedを、どんなものにも割り当て可能なものとして扱っていました。 実際には、nullundefinedは全ての型で有効な値であり、明確に除外することはできませんでした。 (そのため、誤った使用を検知することができませんでした)

--strictNullChecks

--strictNullChecksを指定すると、新しい厳格(strict)なnull検査モードに切り替わります。

厳格なnull検査モードでは、nullundefined値は全ての型の領域から外れ、 自分自身とanyにしか割り当てることができません。 (1つ例外があり、undefinedvoidに対して割当可能です。)

そのため、通常の型検査モードでTT | undefinedが同義であるとみなされる一方で(undefinedは、あらゆるTの部分型(subtype)とみなされるため)、 厳格な型検査モードでは異なる型と判定され、T | undefinedでのみundefined値が許可されます。 TT | nullの関係性にも同じことが言えます。

// Compiled with --strictNullChecks
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1;  // Ok
y = 1;  // Ok
z = 1;  // Ok
x = undefined;  // Error
y = undefined;  // Ok
z = undefined;  // Ok
x = null;  // Error
y = null;  // Error
z = null;  // Ok
x = y;  // Error
x = z;  // Error
y = x;  // Ok
y = z;  // Error
z = x;  // Ok
z = y;  // Ok

使用前に割り当てをチェック

厳格なnull検査では、コンパイラはコードパスで可能な限りのローカル変数に対し、 前もって割り当てがされることでundefinedを含まないローカル変数であることを要求とします。

// Compiled with --strictNullChecks
let x: number;
let y: number | null;
let z: number | undefined;
x;  // エラー、代入されていないものに対する参照
y;  // エラー、代入されていないものに対する参照
z;  // Ok
x = 1;
y = null;
x;  // Ok
y;  // Ok

コンパイラは、型分析に基づく制御フローを実行することで、変数に確実に値が割り当てられているかをチェックします。 詳細については、このトピック内で後述します。

任意のパラメーターとプロパティ

任意のパラメーターとプロパティは、それらの型が明確にundefinedを含まないと型アノテーションされていたとしても、 自動的にundefinedを持ってしまいます。 例えば、下記の2つの型は同義になります。

// Compiled with --strictNullChecks
type T1 = (x?: number) => string;              // x has type number | undefined
type T2 = (x?: number | undefined) => string;  // x has type number | undefined

nullまたはundefinedでは無いことを保証する型の保護(type guards)

プロパティへのアクセスまたは関数の呼び出しは、 そのオブジェクトまたは関数がnullまたはundefinedを含む型の場合にはコンパイル時エラーになります。 ただし、型の保護(type guards)がnullまたはundefinedでは無いことを検査するように拡張されます。

// Compiled with --strictNullChecks
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
    f(x);  // Ok, type of x is number here
}
else {
    f(x);  // Error, type of x is number? here
}
let a = x != null ? f(x) : "";  // aの型は文字列
let b = x && f(x);              // bの型は、string、0、null、undefinedのいずれか

非nullと非undefinedの型の保護(type guards)はnullまたはundefinedと比較するために、 x != nullまたはx === undefinedのようにして、 ==!====!==演算子を使用しても構いません。

対象の変数型への影響は、JavaScriptのセマンティクスを正確に反映します。 (例えば 2重イコール演算子はどちらが指定されていても両方の値をチェックしますが、3重イコール演算子は指定された値のみをチェックします)

ドット付きの名前の型の保護(type guards)

以前は型の保護(type guards)は、ローカル変数とパラメーターのチェックのみをサポートしていましたが、 1つ以上のパラメーターのアクセスに続く変数またはパラメーターから構成される、"ドット付きの名前"のチェックをサポートするようになりました。

interface Options {
    location?: {
        x?: number;
        y?: number;
    };
}

function foo(options?: Options) {
    if (options && options.location && options.location.x) {
        const x = options.location.x;  // xの型はnumber
    }
}

ドット付きの名前の型の保護は、ユーザー定義の型の保護(type guard)関数と、 typeofおよびinstanceof演算子でも動作し、--strictNullChecksコンパイラオプションに依存しません。

ドット付きの名前への型の保護は、ドット付きの名前の任意の部分に代入した後は効果がありません。 例えばx.y.zへの型の保護は、xx.yx.y.zへの割り当ての後は作用しなくなります。

式演算子

式演算子には、オペランド型にnullおよび/またはundefinedを含めることができますが、 必ずnullundefined以外の型の値を生成します。

// Compiled with --strictNullChecks
function sum(a: number | null, b: number | null) {
    return a + b;  // number型の値を生成
}

&&演算子は、左オペランドの型が提供されているかどうかに応じて、 右オペランドの型に対してnullおよび/またはundefinedが追加され、 ||演算子は、共用体型の結果に応じて、左オペランドの型からnullundefinedの両方を削除します。

// Compiled with --strictNullChecks
interface Entity {
    name: string;
}
let x: Entity | null;
let s = x && x.name;            // sの型は string | null
let y = x || { name: "test" };  // yの型は Entity

型の拡大

厳格なnull検査モードでは、null型とundefined型はany型に拡大されません。

let z = null;  // zの型はnull

通常の型検査モードでは、zの型は型の拡大によりanyと推論されますが、 厳格なnull検査モードではzの型はnullと推論されます。 (そのため、型の注釈がない場合はnullzの唯一の値になります。)

非null(Non-null)アサーション演算子

新しい後ろに付加する(post-fix)式演算子である!は、 型チェッカーが断定することが出来ない状況で、そのオペランドが非null、非undefinedであることを主張することに使用できます。

具体的には演算子x!は、xの型の値をnullundefinedを除外したもので生成します。 <T>xx as Tの型注釈(type assertion)と同様に、 !の非nullアサーションは、出力されるJavaScriptコードから排除されます。

// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
    // nullまたは不正なentityの場合は例外をスローする処理を書く
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // eは非nullであり、nameへ確実にアクセスする
}

互換性

新しい機能は、厳格なnull検査モードと通常の型検査モードの両方で使用できるように設計されています。 特に、null型とundefined型は通常の型検査モードでは共用体型から自動的に消去され(他の全ての型の部分型(subtype)であるため)、 !の非nullアサーション演算子は残されますが、通常の型検査モードでは何も作用しません。

そのため、null型とundefined型を使用するように更新された宣言ファイルは、 下位互換性のため通常の型検査モードで引き続き使用できます。

実際には、厳格なnull検査モードでは、コンパイルされる全てのファイルがnull型とundefined型に対応している必要があります。

制御フローベースの型分析

TypeScript 2.0では、ローカルの変数とパラメーターのための制御フローベースの型分析が実装されました。 以前は型の保護(type guards)のための型分析は、if文と?:条件式に限定されて実行されていて、 代入やreturnbreak文のような制御フローの構成は考慮されていませんでした。

TypeScript 2.0では、型チェッカーは出来る限りの全ての文と式の制御フローを分析し、 共用体型(union type)であることを宣言されたローカル変数やパラメーターに、その場所での最大限の具体的な型(絞り込まれた型)を作り出してくれます。

function foo(x: string | number | boolean) {
    if (typeof x === "string") {
        x; // ここでのxの型は string
        x = 1;
        x; // ここでのxの型は number
    }
    x;     // ここでのxの型は number | boolean here
}

function bar(x: string | number) {
    if (typeof x === "number") {
        return;
    }
    x; // ここでのxの型は string
}

nullの代入が可能な型は共用体型(union types)を使用して表されるため、 制御フローベースの型分析は--strictNullChecksと特に関係性が深くなります。

function test(x: string | null) {
    if (x === null) {
        return;
    }
    x; // 関数のこれ以降のxの型は string
}

さらに--strictNullChecksモードでは、 undefinedの値を許さない型を持つローカル変数の代入の分析が、 制御フローベースの型分析に含まれます。

function mumble(check: boolean) {
    let x: number; // undefinedを許さない型
    x;             // エラー、xはundefined
    if (check) {
        x = 1;
        x; // Ok
    }
    x; // エラー、xはundefinedの可能性がある
    x = 2;
    x; // Ok
}

タグ付き共用体型(Tagged union types)

TypeScript 2.0で、タグ付き(または区別された)共用体型(union types)のサポートが実装されました。 具体的には、TSコンパイラは判別プロパティの検証に基づいて共用体型(union types)を絞り込み、 switch文の能力を拡張する、型の保護(type guards)の機能をサポートします。

interface Square {
    kind: "square";
    size: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape) {
    // 下記のswitch文は判別されるプロパティの値に沿って、
    // sの型が各case句で絞り込まれるため、
    // 型のアサーション無しで他のプロパティにアクセス可能になります。
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.width * s.height;
        case "circle": return Math.PI * s.radius * s.radius;
    }
}

function test1(s: Shape) {
    if (s.kind === "square") {
        s;  // Square
    }
    else {
        s;  // Rectangle | Circle
    }
}

function test2(s: Shape) {
    if (s.kind === "square" || s.kind === "rectangle") {
        return;
    }
    s;  // Circle
}

判別プロパティの型の保護(type guard)とは、x.p == vx.p === vx.p != vx.p !== vの形式の式のことです。 ここでp及びvは、文字列リテラル型または文字列リテラル型の共用体型のプロパティ及び式になります。

判別プロパティの型の保護(type guard)はxの型を、 vの値を有することが可能な判別プロパティpを持つ、xを構成する型に絞り込みます。

現在、文字列リテラル型の判別プロパティしかサポートしていないことに注意してください。 今後、真偽値と数値のリテラル型のサポートが追加される予定です。

never型

TypeScript 2.0で、新しいプリミティブな型であるneverが導入されました。 never型は、決して発生することが無いことを示す型になります。 具体的には、neverは決して戻り値を返さない関数の戻り値の型であり、 また、決してtrueになることがない型の保護(type guards)下での変数の型です。

never型には下記の特徴があります。

  • neverは全ての型の部分型(subtype)であり、また割り当てが可能である。
  • いかなる型もneverの部分型(subtype)、また割り当てることができない(never自身を除く)。
  • 戻り値の型アノテーションの無い関数式またはアロー関数で、 もし関数がreturn文を持たない、またはnever型の式のreturn文しか持たず、 もし関数の終点が到達不可能(制御フロー分析による判定)である場合、関数の戻り値の型はneverであると推論されます
  • 関数で明確にneverを戻り値の型としてアノテーションしている場合、 全てのreturn文(もしあれば)は、never型の式を持ち、 関数の終点には到達できないようにしなければいけません。

neverは全ての型の部分型(subtype)であるため、 共用体型(union types)からは常に除外され、関数の戻り値の型の推論は他の型が返されている限り無視されます。

下記はneverを返す関数の例になります。

// 関数がneverを返すには、到達不可能な終点を持たなければいけません
function error(message: string): never {
    throw new Error(message);
}

// 戻り値の型はneverと推論される
function fail() {
    return error("Something failed"); //※errorは上記に定義されているもの
}

// 関数がneverを返すには、到達不可能な終点を持たなければいけません
function infiniteLoop(): never {
    while (true) {
    }
}

下記は、neverを返す関数例です。

// 戻り値の型はnumberと推論
function move1(direction: "up" | "down") {
    switch (direction) {
        case "up":
            return 1;
        case "down":
            return -1;
    }
    return error("Should never get here");
}

// 戻り値の型はnumberと推論
function move2(direction: "up" | "down") {
    return direction === "up" ? 1 :
        direction === "down" ? -1 :
        error("Should never get here");
}

// 戻り値の型はTと推論
function check<T>(x: T | undefined) {
    return x || error("Undefined value");
}

neverは全ての型に割り当て可能であるため、 より絞り込まれた型を返すコールバックが必須となる場合、neverを返す関数を使用することができます。

function test(cb: () => string) {
    let s = cb();
    return s;
}

test(() => "hello");
test(() => fail());
test(() => { throw new Error(); })

読み取り専用プロパティとインデックスシグネチャ

読み取り専用であるとみなすreadonlyを使用して、プロパティまたはインデックスシグネチャが宣言できるようになりました。

読み取り専用のプロパティには初期化子が含まれ、同じクラス宣言の中のコンストラクタで代入されることがありますが、 それ以外の場合は読み取り専用のプロパティへの代入は許可されません。

さらに、エンティティは暗黙的にいくつかの状況下で読み取り専用になります。

  • getアクセサが宣言され、setアクセサが宣言されていないプロパティは、読み取り専用とみなされます。
  • enumオブジェクトの型で、enumメンバは読み取り専用プロパティとみなされます。
  • モジュールオブジェクトの型で、exportされるconst変数は読み取り専用プロパティとみなされます。
  • import文の中で宣言されたエンティティは、読み取り専用とみなされます。
  • ES2015の名前空間のインポートを通じてアクセスされるエンティティは、読み取り専用とみなされます。 (例えば、fooがimport * as foo from "foo"として宣言された場合、foo.xは読み取り専用になります)
interface Point {
    readonly x: number;
    readonly y: number;
}

var p1: Point = { x: 10, y: 20 };
p1.x = 5;  // Error, p1.x is read-only

var p2 = { x: 1, y: 1 };
var p3: Point = p2;  // Ok, read-only alias for p2
p3.x = 5;  // Error, p3.x is read-only
p2.x = 5;  // Ok, but also changes p3.x because of aliasing
class Foo {
    readonly a = 1;
    readonly b: string;
    constructor() {
        this.b = "hello";  // コンストラクタでは代入は許可される
    }
}
let a: Array<number> = [0, 1, 2, 3, 4];
let b: ReadonlyArray<number> = a;
b[5] = 5;      // Error, elements are read-only
b.push(5);     // Error, no push method (because it mutates array)
b.length = 3;  // Error, length is read-only
a = b;         // Error, mutating methods are missing

関数のためのthisの型の指定

クラスまたはインタフェースでthisの型が指定できるようになったことに続いて、 関数とメソッドがそれらが期待するthisの型を宣言できるようになりました。

デフォルトでは、関数内部のthisの型はanyになります。 TypeScript 2.0からは、明確にthisのパラメーターを提供できるようになりました。 関数のパラメーターの1つ目のthisパラメーターは偽のパラメーターです。

function f(this: void) {
    // このスタンドアローンな関数内では、'this'を使用できないようにする
}

コールバック内部のthisパラメーター

ライブラリはthisパラメータを使用して、コールバックの呼び出し方を宣言することもできます。

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void;
}

this: voidaddClickListenerが、onclickthisの型を必要としない関数であることを期待することを意味します。

thisを使用した呼び出しのコードに型の注釈を付けると、下記のようになります。 (訳注: ランタイム時のクラッシュ前に、コンパイル時点でaddClickListenerでエラーになり、予防できるということだと思われます)

class Handler {
    info: string;
    onClickBad(this: Handler, e: Event) {
        // このthisはコールバック元であり、infoプロパティを持たないため、ランタイム時にクラッシュします
        this.info = e.message;
    };
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // エラー!

--noImplicitThis

TypeScript 2.0で新しいフラグも追加され、明確な型注釈の無い関数内で使用される全てのthisに対してフラグを立てます。

tsconfig.jsonでのglobサポート

globがサポートされました! globのサポートは数多く寄せられたリクエストのうちの1つでした。

globライクなファイルパターンは、2つのプロパティ"include""exclude"でサポートされます。

{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "outFile": "../../built/local/tsc.js",
        "sourceMap": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts"
    ]
}

サポートされるグロブのワイルドカードは下記の通りです。

  • * 0字以上の文字(ディレクトリのセパレーターは除く)
  • ? 任意の1文字(ディレクトリのセパレーターは除く)
  • **/ 任意のサブディレクトリに再帰的にマッチ

globパターンのセグメントが*または.*のみを含む場合、 サポートされる拡張子を持つファイルのみが含まれることになります。 (例: デフォルトで.ts.tsx.d.tsが、allowJsをtrueに設定すると、.js.jsx)

"files""include"の両方が指定されていない場合、 コンパイラは"exclude"プロパティによって除外されるファイルを除く、 ディレクトリとサブディレクトリ内の全てのTypeScript(.ts.d.ts.tsx)ファイルを含めます。 allowJstrueに設定されている場合は、JSファイル(.jsおよび.jsx)も含まれます。

"files"または"include"プロパティが指定されている場合は、 コンパイラはそれらの2つのプロパティに含まれるファイルの和集合を代わりに含めるようにします。

"outDir"のコンパイラオプションを使用して指定されたディレクトリ内のファイルは、 "files"プロパティによって明示的に指定されていない限り("exclude"プロパティが指定されていても)、常に除外されます。

"include"を使用して含まれるファイルは、"exclude"プロパティを使用してフィルタリングできます。 ただし、"files"プロパティを使用して明示的に含まれるファイルは、"exclude"に関係なく常に含まれます。 "exclude"プロパティは何も指定されない場合は、 node_modulesbower_componentsjspm_packagesディレクトリを除外します。

BaseUrl、パスマッピング、rootDirs、追跡によるモジュール解決機能の向上

TypeScript 2.0には、追加のモジュール宣言がどこにあるかをコンパイラに通知するための、 モジュール解決の仕組みが用意されています。

詳細については、モジュール解決を参照してください。

Base URL

baseUrlはAMDモジュールローダーを使用するアプリケーションで、 実行時に特定のフォルダにモジュールを"デプロイ"するための一般的な慣習です。 相対的な名前が指定されない全てのモジュールのインポートは、baseUrlの相対であるとみなされます。

{
  "compilerOptions": {
    "baseUrl": "./modules"
  }
}

"moduleA"をインポートすると、./modules/moduleAで解決されます。

import A from "moduleA";

パスマッピング

モジュールがbaseUrl下に直接配置されていないことがあります。 ローダーは実行時にモジュール名をファイルにマッピングするために、マッピング設定を使用します。 RequireJSのドキュメントと、 SystemJSのドキュメントを参照してください。

TypeScriptコンパイラは、tsconfig.jsonファイルの"paths"プロパティを使用して、 そのようにマッピングされる宣言をサポートします。

例えば、"jquery"モジュールのインポートは、 実行時に"node_modules/jquery/dist/jquery.slim.min.js"に変換されます。

{
  "compilerOptions": {
    "baseUrl": "./node_modules",
    "paths": {
      "jquery": ["jquery/dist/jquery.slim.min"]
    }
}

"paths"を使用すると複数のフォールバックの位置を含む、より洗練されたマッピングも可能になります。 いくつかのモジュールが特定の場所でしか利用できず、残りのモジュールが別の場所にあるようなプロジェクトの場合に検討してください。

rootDirsを使用した仮想ディレクトリ

'rootDirs'を使用すると、"仮想(virtual)"ディレクトリを構成するルートをコンパイラに伝えることができます。 コンパイラは、それらの"仮想"ディレクトリ内の相対モジュールのインポートを、 1つのディレクトリにまとめてマージしたかのように解決してくれます。

次のようなプロジェクト構成が与えられている場合、

 src
 └── views
     └── view1.ts (imports './template1')
     └── view2.ts

 generated
 └── templates
         └── views
             └── template1.ts (imports './view2')

ビルド時には、/src/views/generated/templates/viewsのファイルが、 出力用の同じディレクトリにコピーされます。 ランタイム時には、ビュー(view)はそのテンプレート(template)が同じディレクトリ内に存在することを期待し、 "./template"として相対的な名前を使用して、それをimportするはずです。

"rootDirs"には、実行時にマージさせたいコンテンツが含まれるルートのリストを指定します。 この例であれば、tsconfig.jsonは次のようになります。

{
  "compilerOptions": {
    "rootDirs": [
      "src/views",
      "generated/templates/views"
    ]
  }
}

モジュール解決の追跡

--traceResolutionは、モジュールがコンパイラによってどのように解決されたのかを教えてくれる手軽な方法を提供してくれます。

tsc --traceResolution

簡略(shorthand)アンビエントモジュール宣言

新しいモジュールを使う前に、宣言を書くことで時間を取らないようにするために、 簡略(shorthand)宣言を使用してすぐにモジュールを使用することができるようになりました。

declarations.d.ts
declare module "hot-new-module";

簡略モジュールからインポートした全てのモジュールは、any型になります。

import x, {y} from "hot-new-module";
x(y);

モジュール名でのワイルドカード文字

以前はモジュールローダー拡張(例: AMDまたはSystemJS)を使用して、 コードでは無いリソースをインポートすることは簡単ではありませんした。

TypeScript 2.0は、ワイルドカード文字(*)を使用してモジュール名の"ファミリー"の宣言をサポートします。 この方法では、宣言は拡張のために1度だけ必要であり、全てのリソースで必要とされません。

declare module "*!text" {
    const content: string;
    export default content;
}
// Some do it the other way around.
declare module "json!*" {
    const value: any;
    export default value;
}

これで、"*!text"または"json!*"にマッチするものをインポートできるようになります。

import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);

ワイルドカードのモジュール名は、型の無いコードからマイグレーションする際にも非常に便利です。 簡略(shorthand)アンビエントモジュール宣言と連携すると、モジュールはanyとして簡単に宣言することができます。

declare module "myLibrary/*";

myLibrary下でのモジュールの全てのインポートは、コンパイラによってany型とみなされます。 したがって、これらモジュールの形状や型の検査は行われません。

import { readFile } from "myLibrary/fileSystem/readFile`;

readFile(); // readFile is 'any'

UMDモジュール定義のサポート

ライブラリの中には、多くのモジュールローダーに使用されるか、 モジュール読み込みを使用しない(グローバル変数)ように設計されたものがあります。

これらは、UMDまたはIsomorphicモジュールとして知られています。 これらのライブラリは、インポートまたはグローバル変数のどちらかでアクセスすることができます。

例えば、

math-lib.d.ts
export const isPrime(x: number): boolean;
export as namespace mathLib;

ライブラリは、モジュール内でのインポートとして使用できます。

import { isPrime } from "math-lib";
isPrime(2);
mathLib.isPrime(2); // ERROR: can't use the global definition from inside a module

グローバル変数として使用することができますが、スクリプト内に限られます。(スクリプトとは、importまたはexportの無いファイル)

mathLib.isPrime(2);

任意のクラスプロパティ

インタフェースで既に可能であることと同様に、プロパティとメソッドが任意であることをクラスで宣言できるようになりました。

class Bar {
    a: number;
    b?: number;
    f() {
        return 1;
    }
    g?(): number;  // 任意のメソッドの本文は省略可能です
    h?() {
        return 2;
    }
}

--strictNullChecksモードでコンパイルされた場合、任意のプロパティとメソッドは自動的にその型にundefinedを含めます。 そのため、上記のbプロパティはnumber | undefinedの型となり、 gメソッドは(() => number) | undefinedの型になります。 型の保護(type guards)で、型のundefined部分を取り除くことが可能です。

function test(x: Bar) {
    x.a;  // number
    x.b;  // number | undefined
    x.f;  // () => number
    x.g;  // (() => number) | undefined
    let f1 = x.f();            // number
    let g1 = x.g && x.g();     // number | undefined
    let g2 = x.g ? x.g() : 0;  // number
}

privateとprotectedのコンストラクタ

クラスのコンストラクタは、privateまたはprotectedを印付けすることができます。

privateコンストラクタのクラスは、クラス本文の外からインスタンス化することができず、継承されることも不可です。 protectedコンストラクタのクラスは、クラス本文の外からインスタンス化することができませんが、継承されることも可能です。

class Singleton {
    private static instance: Singleton;

    private constructor() { }

    static getInstance() {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
}

let e = new Singleton(); // Error: constructor of 'Singleton' is private.
let v = Singleton.getInstance();

抽象プロパティと抽象アクセサ

抽象(abstract)クラスは、抽象プロパティ、抽象アクセサを宣言することができます。 サブクラスは抽象プロパティを宣言するか、abstractとして印付けされる必要があります。 抽象プロパティは初期化子を持つことができません。 抽象アクセサは本文を持つことができません。

abstract class Base {
    abstract name: string;
    abstract get value();
    abstract set value(v: number);
}

class Derived extends Base {
    name = "derived";

    value = 1;
}

暗黙のインデックスシグネチャ

オブジェクト・リテラルの全ての既知のプロパティが、そのインデックスシグネチャに割り当て可能である場合、 オブジェクト・リテラルの型はインデックス・シグネチャを持つ型に割り当て可能になりました。

これにより、オブジェクトリテラルで初期化された変数を、 マップ(またはディクショナリ)を必要とする関数にパラメーターとして渡すことが可能になりました。

function httpService(path: string, headers: { [x: string]: string }) { }

const headers = {
    "Content-Type": "application/x-www-form-urlencoded"
};

httpService("", { "Content-Type": "application/x-www-form-urlencoded" });  // Ok
httpService("", headers);  // 以前はできませんでしたが、現在はOkになりました。

--libを使用した組み込み型宣言の読み込み

ES6/ES2015の組み込みAPI宣言の使用は、target: ES6の場合に限られていました。 --libが導入されたことで、プロジェクトに含める組み込みAPI宣言のグループの一覧を選択できるようになりました。

例えば、もしMapSetPromiseがランタイム時にサポートされることが期待できる場合、 --lib es2015.collection,es2015.promiseを含めます。 同様にプロジェクトに含めたくない宣言を除外することもできます。 例えば、もしあなたが--lib es5、es6を使用したnodeのプロジェクトに取り組んでいるなら、DOMがそれに該当するでしょう。 (訳注: --lib es5、es6と指定するとDOMが除外されるということだと思われます)

下記が利用可能なAPIグループの一覧になります。

  • dom
  • webworker
  • es5
  • es6 / es2015
  • es2015.core
  • es2015.collection
  • es2015.iterable
  • es2015.promise
  • es2015.proxy
  • es2015.reflect
  • es2015.generator
  • es2015.symbol
  • es2015.symbol.wellknown
  • es2016
  • es2016.array.include
  • es2017
  • es2017.object
  • es2017.sharedmemory
  • scripthost
tsc --target es5 --lib es5,es2015.promise
"compilerOptions": {
    "lib": ["es5", "es2015.promise"]
}

--noUnusedParametersと--noUnusedLocalsを使用した未使用の宣言へのフラグ

TypeScript 2.0では、コードを綺麗にメンテナンスするのに役立つ2つの新しいフラグが追加されました。 --noUnusedParametersは、使用されていない関数またはメソッドのパラメーターに対して、エラーのフラグを立てます。

--noUnusedLocalsは、変数、関数、クラス、インポート他、使用されていないローカル(exportされていない)宣言に対してフラグを立てます。 また、--noUnusedLocalsはクラスで使用されていないprivateメンバに対してもエラーのフラグを立てます。

import B, { readFile } from "./b";
//     ^ Error: `B` declared but never used
readFile();


export function write(message: string, args: string[]) {
    //                                 ^^^^  Error: 'arg' declared but never used.
    console.log(message);
}

_で始まる名前のパラメーター宣言は、未使用パラメーター検査から免除されます。

function returnNull(_a) { // OK
    return null;
}

モジュール識別子による.js拡張子の許可

TypeScript 2.0以前は、モジュール識別子は常に拡張子無しとみなされていました。 例えば、import d from "./moduleA.js"としてインポートすると、 コンパイラは./moduleA.js.tsまたは./moduleA.js.d.ts内の"moduleA.js"の定義を探していました。 これは、モジュール識別子がURIであることを要求するSystemJSのようなバンドル/ローディングツールを使いにくいものにしていました。

TypeScript 2.0では、./moduleA.tsまたは./moduleA.d.ts"moduleA.js"の定義を探すようになります。

'target : es5'と'module: es6'の組み合わせのサポート

以前は、不正なフラグの組み合わせとされていたtarget: es5module: es6が、サポートされるようになりました。 これにより、rollupのような、 ES2015ベースのツリーシェイカーの使用が容易になります。

関数パラメータと引数リストのトレイリング・カンマ

関数のパラメーター内と引数リストのトレイリング(末尾)カンマが許可されるようになりました。 これは、ES3/ES5/ES6で有効にするStage-3 ECMAScript提案の実装です。

function foo(
  bar: Bar,
  baz: Baz, // trailing commas are OK in parameter lists
) {
  // Implementation...
}

foo(
  bar,
  baz, // and in argument lists
);

新コンパイラオプション --skipLibCheck

TypeScript 2.0では、--skipLibCheckのコンパイラオプションが追加され、 宣言ファイル(拡張子.d.tsを持つファイル)の型チェックをスキップできるようになりました。

プログラムに大きな宣言ファイルが含まれている場合、 コンパイラはエラーが無いことが既に分かっている宣言の型の検査に多くの時間を費やし、 また宣言ファイルの型検査を式ップすることで、コンパイル時間を大幅に短縮することができます。

あるファイルの宣言が他のファイルの型チェックに影響する可能性があるため、 --skipLibCheckが指定されているとエラーが検出されないことがあります。 例えば、非宣言ファイルが宣言ファイルで宣言された型を補完する場合、 宣言ファイルがチェックされたときにのみエラーが報告されることがあります。 ただし、そのようなケースは稀でしょう。

宣言を跨いだ重複識別子の許可

インターフェイスに同じメンバを定義する複数の宣言ファイルなどは、 重複定義エラーの一般的な原因の1つでしょう。

同じブロック内での宣言の重複は、引き続き許可されません。

interface Error {
    stack?: string;
}

interface Error {
    code?: string;
    path?: string;
    stack?: string;  // OK
}

新コンパイラオプション --declarationDir

--declarationDirを使用すると、JavaScriptファイルとは異なる場所に宣言ファイルを生成できます。

 Back to top

© https://github.com/Microsoft/TypeScript-Handbook

このページは、ページトップのリンク先のTypeScript-Handbook内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。

  • ドキュメントの情報が古い可能性があります。
  • "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。