TypeScript 1.5

ES6モジュール

TypeScript 1.5は、ECMAScript 6(ES6)のモジュールをサポートします。

ES6モジュールは新しい構文によるTypeScriptの外部モジュールの仕組みになります。 ES6モジュールは他のモジュールをインポート可能な個別に読み込まれるソースファイルであり、 また外部からアクセス可能なエクスポートも提供します。

ES6モジュールには、新しいエクスポートとインポートの宣言が備わっています。 TypeScriptのライブラリとアプリケーションを新しい構文を使用したものに更新することをお勧めしますが、必須ではありません。

新しいES6モジュールの文法は、TypeScriptの元の内部モジュールと外部モジュールの構成と共存し、自由に組み合わせることができます。

export宣言

export宣言を装飾するための既存のTypeScriptサポートに加えて、 as句で任意に異なる名前をエクスポートに指定した別のexport宣言を使用して、 モジュールのメンバをエクスポートさせることも可能です。

interface Stream { ... }
function writeToStream(stream: Stream, data: string) { ... }
export { Stream, writeToStream as write };  // writeToStreamはwriteとしてエクスポートされる

インポート宣言は、インポートに対して別のローカル名を指定するために、任意でas句を使用することができます。   下記はその例になります。

import { read, write, standardOutput as stdout } from "./inout";
var s = read(stdout);
write(stdout, s);

個別にインポートする代わりに、名前空間のインポートを使用して、モジュール全体をインポートすることができます。

import * as io from "./inout";
var s = io.read(io.standardOutput);
io.write(io.standardOutput, s);

再エクスポート

from句を使用することで、ローカルの名前を取り入れることなく、 指定されたモジュールのエクスポートを現在のモジュールにコピーすることができます。

export { read, write, standardOutput as stdout } from "./inout";

export *を使用することで、別のモジュールの全てのエクスポートを再エクスポートすることができます。      これは、他のいくつかのモジュールのエクスポートを集約するモジュールを作成する場合に便利です。

export function transform(s: string): string { ... }
export * from "./mod1";
export * from "./mod2";

デフォルトエクスポート

エクスポートのデフォルト(default)宣言は、モジュールのデフォルトのエクスポートとなる式を指定します。

export default class Greeter {
    sayHello() {
        console.log("Greetings!");
    }
}

下記は、デフォルトのインポートを使用しています。

import Greeter from "./greeter";
var g = new Greeter();
g.sayHello();

むき出し(Bare)のインポート

"むき出しのインポート"は副作用を伴うだけです。

import "./polyfills";

モジュールについての詳細は、 ES6 module support specを参照してください。

分割宣言と分割割り当て

TypeScript 1.5は、ES6の「分割宣言と分割割り当て(Destructuring in declarations and assignments)」をサポートします。

宣言

分割宣言は、1つ以上の名前付きの変数を取り入れて、オブジェクトのプロパティまたは配列の要素から取り出される値で初期化します。

例えば、下記の例では変数xyzが宣言され、 それぞれgetSomeObject().xgetSomeObject().ygetSomeObject().zで初期化されます。

var { x, y, z} = getSomeObject();

分割宣言は、配列から取り出した値に足しても動作します。

var [x, y, z = 10] = getSomeArray();

同様に、分割は関数の引数宣言でも使用することができます。

function drawText({ text = "", location: [x, y] = [0, 0], bold = false }) {
    // Draw text
}

// Call drawText with an object literal
var item = { text: "someText", location: [1,2,3], style: "italics" };
drawText(item);

割り当て

分割パターンは通常の割り当て式にも使用することができます。 例えば、2つの変数の交換を1つの分割割り当てとして書くことができます。

var x = 1;
var y = 2;
[x, y] = [y, x];

namespaceキーワード

TypeScriptは「内部モジュール」も「外部モジュール」もどちらもmoduleキーワードを使用して宣言していましたが、 これはTypeScritpを始める開発者を少し混乱させるものになっています。

「内部モジュール」は、多くの人が名前空間と呼ぶものに近いものです。 同様に、JSの「外部モジュール」は実際にはただのモジュールでしかありません。

注意: これまでの内部モジュールの定義構文は、引き続きサポートされます。

これまでの書き方
module Math {
    export function add(x, y) { ... }
}
このバージョン以降の書き方
namespace Math {
    export function add(x, y) { ... }
}

letとconstのサポート

ES3とES5を対象とした際に、ES6のletconst宣言がサポートされるようになりました。

Const
const MAX = 100;

++MAX; // Error: The operand of an increment or decrement
       //        operator cannot be a constant.
Block scoped
if (true) {
  let a = 4;
  // use a
}
else {
  let a = "string";
  // use a
}

alert(a); // Error: a is not defined in this scope

for..ofのサポート

TypeScript 1.5で、ES6を対象にした際にサポートされるイテレーターインターフェースが、 ES3/ES5でも同様に完全にサポートされ、ES6のfor..ofの配列ループが使用できるようになりました。

TypeScriptコンパイラは、ES3/ES5を対象とする場合、 for..ofを慣用的に使用されるこれらのバージョンのJavaScriptにトランスパイルします。

for (var v of expr) { }

これは次のように出力されます。

for (var _i = 0, _a = expr; _i < _a.length; _i++) {
    var v = _a[_i];
}

デコレータ

TypeScriptのデコレータは、ES7 decorator proposalが元になっています。

デコレータとは、

  • 式である
  • 関数を評価する
  • 対象、名前、プロパティ記述子を引数として受け取る
  • 任意で対象のオブジェクトに取り付けるプロパティ記述子を返す

詳細については、Decorators proposalを参照してください。

デコレータreadonlyenumerable(false)は、 プロパティメソッドがクラスCに取り入れられる前に、そのプロパティメソッドに適用されます。

これはデコレータによる実装の変更を可能とし、 このケースでは記述子に対して、writable(書き込み可能): false、enumerable(列挙可能): falseの補強を行っています。

class C {
  @readonly
  @enumerable(false)
  method() { }
}

function readonly(target, key, descriptor) {
    descriptor.writable = false;
}

function enumerable(value) {
  return function (target, key, descriptor) {
     descriptor.enumerable = value;
  }
}

計算されたプロパティ(Computed properties)

動的プロパティを使用したオブジェクトの初期化は、多少の面倒が伴うケースが少なからずあります。 例えば、次のような例です。

type NeighborMap = { [name: string]: Node };
type Node = { name: string; neighbors: NeighborMap;}

function makeNode(name: string, initialNeighbor: Node): Node {
    var neighbors: NeighborMap = {};
    neighbors[initialNeighbor.name] = initialNeighbor;
    return { name: name, neighbors: neighbors };
}

ここでは、初期化のためにneighbor-mapを保持する変数を作成する必要があります。 TypeScript 1.5であれば、この面倒な作業をコンパイラに押し付けることができます。

function makeNode(name: string, initialNeighbor: Node): Node {
    return {
        name: name,
        neighbors: {
            [initialNeighbor.name]: initialNeighbor
        }
    }
}

UMDとSystemモジュール出力のサポート

AMDCommonJSのモジュールローダーに加え、 TypeScriptはUMD(Universal Module Definition)とSystemのモジュールフォーマットの出力をサポートするようになりました。

使用方法

tsc --module umd
tsc --module system

文字列のユニコード符号位置のエスケープ

ES6では、単一のエスケープを使用してUnicodeのコードポイントを表現できるエスケープが導入されています。

たとえば、文字「𠮷」を含む文字列をエスケープする必要があるとします。 UTF-16/UCS2では、'𠮷'はサロゲートペアとして表されます。 つまり、16ビットのコード単位のペア、具体的には0xD8420xDFB7を使用してエンコードされています。

以前は、コードポイントを"\uD842\uDFB7"としてエスケープする必要がありましたが、 これにはサロゲートペアから2つの独立した文字を識別することが難しいという大きな欠点があります。

ES6のコードポイントをエスケープすると、"\u{20bb7}"という単一のエスケープで、文字列とテンプレート文字列の正確な文字をきちんと表現できます。 TypeScriptはES3/ES5では、"\uD842\uDFB7"として文字列の出力を行います。

ES3/ES5におけるタグ付けされたテンプレート文字列

TypeScript1.4で、全ての対象に対してテンプレート文字列を、ES6に対してタグ付きテンプレートのサポートを追加しました。 @ivogabeの働きのおかげで、 ES3とES5のタグ付きテンプレートの問題を克服することができました。

ES3/ES5を対象とする場合、下記のコードは

function oddRawStrings(strs: TemplateStringsArray, n1, n2) {
    return strs.raw.filter((raw, index) => index % 2 === 1);
}

oddRawStrings `Hello \n${123} \t ${456}\n world`

次のように出力されるようになりました。

function oddRawStrings(strs, n1, n2) {
    return strs.raw.filter(function (raw, index) {
        return index % 2 === 1;
    });
}
(_a = ["Hello \n", " \t ", "\n world"], _a.raw = ["Hello \\n", " \\t ", "\\n world"], oddRawStrings(_a, 123, 456));
var _a;

AMD依存の任意の名前

/// <amd-dependency path="x" />は、結果的に得られるモジュールのrequire呼び出しで、 注入する必要がある非TSモジュールの依存性についてコンパイラに通知します。 ただし、このモジュールをTSコードで消費する方法がありませんでした。

新しいamd-dependency nameプロパティでは、AMDの依存性に対して任意の名前を渡すことが可能になりました。

/// <amd-dependency path="legacy/moduleA" name="moduleA"/>
declare var moduleA:MyType
moduleA.callStuff()

これは次のようなJSコードで生成されます。

define(["require", "exports", "legacy/moduleA"], function (require, exports, moduleA) {
    moduleA.callStuff()
});

tsconfig.jsonを介したプロジェクトのサポート

ディレクトリにtsconfig.jsonファイルを追加すると、そのディレクトリがTypeScriptプロジェクトのルートであることが示されます。 tsconfig.jsonファイルには、プロジェクトをコンパイルするために必要なルートファイルとコンパイラのオプションを指定します。 プロジェクトは、次のいずれかの方法でコンパイルされます。

  • 入力ファイルの指定無しでtscを実行します。 コンパイラは現在のディレクトリからtsconfig.jsonファイルを検索し、見つかるまで親ディレクトリを辿って検索を続けます。
  • 入力ファイルの指定無し、且つ-project(または、-pのみ)のコマンドライン・オプション付きでtscを実行します。 このオプションにはtsconfig.jsonが含まれるディレクトリのパスを指定します。
{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "sourceMap": true,
    }
}

詳細については、tsconfig.jsonを参照してください。

--rootDirコマンドラインオプション

オプション--outDirは、出力の入力階層を複製します。 コンパイラは、入力ファイルのルートを、全ての入力ファイルの中で最も長い共通パスから算出します。 これを使用して出力内のすべての部分構造を複製します。

時によって、これは望ましくない結果をもたらすことがあります。 例えば、FolderA\FolderB\1.tsFolderA\FolderB\2.tsを入力すると、 FolderA\FolderB\の出力構造が複製されます。 ここで、もし新しいファイルFolderA\3.tsを入力に追加すると、 出力構造が複製されたFolderA\に飛び出してしまいます。

--rootDirは入力ディレクトリを算出する代わりに、入力ディレクトリが出力に複製されるように指定します。

翻訳者より: ここの説明が分かりづらかったので独自に調査してみました。下記のコマンド結果はそれぞれ次のようになります。

3.tsを含めずにコンパイルを実行すると、フォルダ自体は複製されません。

tsc --outDir builds FolderA/FolderB/1.ts FolderA/FolderB/2.ts
├── FolderA
│   ├── 3.ts
│   └── FolderB
│       ├── 1.ts
│       └── 2.ts
└── builds
    ├── 1.js
    └── 2.js

3.tsを含めてコンパイルを実行すると、FolderBだけが複製され、中途半端な状態になります。

tsc --outDir builds FolderA/FolderB/1.ts FolderA/FolderB/2.ts FolderA/3.ts
├── FolderA
│   ├── 3.ts
│   └── FolderB
│       ├── 1.ts
│       └── 2.ts
└── builds
    ├── 3.js
    └── FolderB
        ├── 1.js
        └── 2.js

--rootDirにカレントディレクトリを指定すれば、FolderAFolderBの両方が複製されます。

tsc --rootDir ./ --outDir builds FolderA/FolderB/1.ts FolderA/FolderB/2.ts FolderA/3.ts
├── FolderA
│   ├── 3.ts
│   └── FolderB
│       ├── 1.ts
│       └── 2.ts
└── builds
    └── FolderA
        ├── 3.js
        └── FolderB
            ├── 1.js
            └── 2.js

--noEmitHelpersコマンドラインオプション

TypeScriptコンパイラは、必要に応じて__extendsのような幾つかのヘルパー(関数等)を出力します。 ヘルパーは、参照される全てのファイルで生成されます。

全てのヘルパーを1か所にまとめたり、デフォルトの動作をオーバーライドしたりするには、 --noEmitHelpersを使用してコンパイラがそれらを出力しないように指示します。

--newLineコマンドラインオプション

デフォルトで出力される改行の文字は、Windowsであれば\r\n、*nixベースのシステムであれば\nになります。 --newLineコマンドラインはこの挙動を上書きし、 生成されるファイルに使用される改行文字の指定を可能にしてくれます。

--inlineSourceMapとinlineSourcesコマンドラインオプション

--inlineSourceMapを指定すると、ソースマップファイルは独立した.js.mapファイルではなく、 生成された.jsファイルに埋め込まれます。 --inlineSourcesは更に.jsファイル内に.tsファイルのソースを埋め込みます。

 Back to top

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

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

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