TypeScript 1.8
- 型パラメータの制約
- エラーの制御フロー分析
- Reactのステートレス関数コンポーネント
- Reactのpropsの型管理の簡略化
- モジュールのglobal/moduleスコープの拡張
- 文字列リテラル型
- 共用体(union)/交差(intersection)型推論の改善
- --outFileを使用したAMDとSystemモジュールの連携
- SystemJSを使用したdefault importの相互運用のサポート
- ループ内のlet/constの補足の改善
- for..in文チェックの改善
- モジュールによる"use strict"の文頭出力
- --allowJsに.jsファイルを含める
- --reactNamespaceを使用したカスタムJSXファクトリー
- thisを基にした型の保護
- 公式のTypeSriptのNuGetパッケージ
- tscのエラーメッセージの装飾
- Visual Studio 2015によるJSXコードの色付け
- --project (-p)フラグのファイルパスの受け入れ
- tsconfig.json内へのコメント
- IPC-drivenファイルへの出力のサポート
- Visual Studio 2015のtsconfig.jsonサポートの改善
型パラメータの制約
TypeScript 1.8では、型パラメータの制約が、同じ型パラメータリストのものを参照することが可能になります。 以前はこれはエラーになっていました。 この機能は、一般的にF-Bounded Polymorphismと呼ばれます。
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
エラーの制御フロー分析
TypeScript 1.8では、ユーザーが遭遇しがちな一般的なエラーを捕捉するのに役立つ制御フロー分析が導入されています。 詳細については、下記のエラー動作を確認してください。
到達不可能(Unreachable)なコード
ランタイム時に実行されないことが確実な文は、到達不可能なコードエラーとして正確にフラグされるようになりました。
例えば、無条件に実行されるreturn
、throw
、break
、continue
文の後に続く文は、
到達不可能であるとみなされます。
--allowUnreachableCode
を使用すると、到達不可能なコードの検知・報告を無効化することができます。
例
下記は到達不可能なコードエラーのシンプルな例になります。
function f(x) {
if (x) {
return true;
}
else {
return false;
}
x = 0; // エラー: 到達不可能なコードを検知
}
この機能が検知する、より一般的なエラーに、return
文の後に改行を追加してしまう事例が挙げられます。
function f() {
return // 改行を引き金にして、自動的にセミコロンが挿入される
{
x: "string" // エラー: 到達不可能なコードを検知
}
}
JavaScriptは行末のreturn
文で自動的に終了するので、オブジェクトリテラルはブロックになります。
使用されないラベル(Unused labels)
使用されないラベルにもフラグが付くようになりました。
到達不可能なコードのチェックのように、デフォルトで有効になりますが、
--allowUnusedLabels
を使用すると、これらのエラーの報告が無効になります。
loop: while (x > 0) { // エラー: 使用されないラベル
x++;
}
暗黙的な戻り値
JavaScriptでは値を返さないコードが書かれた関数は、暗黙的にundefined
を返します。
これらはコンパイラによって暗黙的な戻り値を返すものとしてフラグが立てられるようになります。
デフォルトではこのチェックはOFFになっており、--noImplicitReturns
を使用してONにします。
function f(x) { // Error: Not all code paths return a value.
if (x) {
return false;
}
// implicitly returns `undefined`
}
抜け落ちるcase
TypeScriptでは、switch文の中でcaseが空では無い且つ抜け落ちる(breakが無い)ような場合に、エラーを報告することができます。
デフォルトではこのチェックはOFFになっており、--noFallthroughCasesInSwitch
を使用してONにします。
例
--noFallthroughCasesInSwitch
を使用すると、この例はエラーが発せられます。
switch (x % 2) {
case 0: // Error: Fallthrough case in switch.
console.log("even");
case 1:
console.log("odd");
break;
}
ただし下記の例の場合は、抜け落ちるcaseが空であるためエラーは報告されません。
switch (x % 3) {
case 0:
case 1:
console.log("Acceptable");
break;
case 2:
console.log("This is *two much*!");
break;
}
Reactのステートレス関数コンポーネント
TypeScriptは、ステートレス関数コンポーネント(Stateless Function components)をサポートするようになりました。 これらは、他のコンポーネントを容易に構成することができる軽量コンポーネントです。
// Use parameter destructuring and defaults for easy definition of 'props' type
const Greeter = ({name = 'world'}) => <div>Hello, {name}!</div>;
// Properties get validated
let example = <Greeter name='TypeScript 1.8' />;
この機能と簡略化されたpropsの恩恵を受けるには、react.d.tsの最新版を使用してください。
Reactのpropsの型管理の簡略化
最新のreact.d.ts(1つ前を参照)を使用したTypeScript 1.8では、propsの型の宣言を大幅に簡略化することも行いました。
具体的な内容は下記のとおりです。
-
明示的な
ref
とkey
の宣言、またはextend React.Props
は必要はありません。 -
ref
とkey
プロパティは、全てのコンポーネントで正しい型として現れます。 -
ref
プロパティは、ステートレス関数コンポーネントのインスタンス上で、禁止されます。
モジュールのglobal/moduleスコープの拡張
ユーザーは、既存のモジュールに対して自分が作成した機能または誰かが作成した機能を、
拡張することを宣言できるようになりました。
モジュール拡張は、古い素のアンビエントモジュール宣言(つまり、declare module "foo" { }
文)のようであり、
自身のモジュールまたは別のアンビエント外部モジュールのトップ階層を直接的に入れ子にします。
さらに、TypeScriptはdeclare global { }
形式のグローバルな拡張の概念も持ち合わせています。
これは、必要であればモジュールをArray
のようなグローバルな型に拡張することを可能にしてくれます。
モジュール拡張の名前は、import
とexport
宣言で使用されるモジュール指定と同じルールで解決されます。
モジュール拡張の宣言は、同じファイル内で宣言された場合と同じ方法で既存の宣言とマージされます。
モジュールの拡張もグローバルの拡張も、トップ階層のスコープに新しいアイテムを追加することはできません。 これらは既存の宣言に"パッチを当てる"だけに過ぎません。
例
ここでmap.ts
は、observable.ts
からのObservable
型の内部にパッチを当て、
map
メソッドの追加を宣言しています。
// observable.ts
export class Observable<T> {
// ...
}
// map.ts
import { Observable } from "./observable";
// "./observable"の拡張
declare module "./observable" {
// インターフェースのマージで'Observable'のクラス宣言を拡張
interface Observable<T> {
map<U>(proj: (el: T) => U): Observable<U>;
}
}
Observable.prototype.map = /*...*/;
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map(x => x.toFixed());
同様にグローバルスコープは、declare global
宣言を使用してモジュールから拡張することができます。
// Ensure this is treated as a module.
export {};
declare global {
interface Array<T> {
mapToNumbers(): number[];
}
}
Array.prototype.mapToNumbers = function () { /* ... */ }
文字列リテラル型
APIが特定の値に対して特定の文字列を期待するのは珍しいことではありません。 例えば、アニメーション「easing」を制御しながら、 要素が画面を移動するUIライブラリを考えてみましょう。
declare class UIElement {
animate(options: AnimationOptions): void;
}
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: string; // "ease-in", "ease-out", "ease-in-out"が指定可能
}
ただし、これは間違いのもとになります。 ユーザーが正しいイージング値を誤って指定したとしても、それを止めるものは何もありません。
// "ease-in-out"とするべきところを"ease-inout"としているがエラーにならない
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" });
TypeScript 1.8では、文字列リテラル型が導入されました。 これらの型(の値)は文字列リテラルの型の場合と同じように書かれますが、その判定が異なります。
型システムが先程のようなエラーを確実に捕捉し、ユーザーに知らせてくれるようになります。
下記は、文字列リテラル型を使用した新しいAnimationOptions
になります。
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: "ease-in" | "ease-out" | "ease-in-out";
}
// Error: Type '"ease-inout"' is not assignable to type '"ease-in" | "ease-out" | "ease-in-out"'
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" });
共用体(union)/交差(intersection)型推論の改善
TypeScript 1.8では、共用体型と交差型の対象の側と被対象の側の両方を含む型推論が改善されています。
例えば、string | string[]
をstring | T
と推論する際に、
string[]
がT
と推論されることから、string[]
の型をT
に降格します。
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
}
--outFileを使用したAMDとSystemモジュールの連携
--outFile
を--module amd
または--module system
と一緒に指定すると、
コンパイル時に全てのモジュールが、複数のモジュールクロージャを含む単一の出力ファイルに連結されます。
モジュール名は、各モジュールのrootDir
への相対位置を基準にして導きだされます。
// file src/a.ts
import * as B from "./lib/b";
export function createA() {
return B.createB();
}
// file src/lib/b.ts
export function createB() {
return { };
}
define("lib/b", ["require", "exports"], function (require, exports) {
"use strict";
function createB() {
return {};
}
exports.createB = createB;
});
define("a", ["require", "exports", "lib/b"], function (require, exports, B) {
"use strict";
function createA() {
return B.createB();
}
exports.createA = createA;
});
SystemJSを使用したdefault importの相互運用のサポート
SystemJSのようなモジュールローダーはCommonJSモジュールをラップし、
default
のES6のimportとして公開します。
これにより、ローダーに基づくモジュールの形状が異なるものになるため、 SystemJSとCommonJSのモジュール実装の定義ファイルの共有が不可能になります。
新しいコンパイラフラグである--allowSyntheticDefaultImports
を設定すると、
モジュールローダーがインポートされた.tsまたは.d.ts内で示されていない、何らかの合成(Synthetic)default importを実行します。
コンパイラは、モジュール全体の形状を持つdefault
のエクスポートの存在を推論します。
システムモジュールはデフォルトでこのフラグをオンにします。
ループ内のlet/constの補足の改善
以前はエラーになっていましたが、TypeScript 1.8でサポートされるようになりました。
ループ内と関数内で補足されるlet
/const
宣言は、
意味的に正しくマッチするように出力されるようになりました。
let list = [];
for (let i = 0; i < 5; i++) {
list.push(() => i);
}
list.forEach(f => console.log(f()));
上記は下記のようにコンパイルされます。
var list = [];
var _loop_1 = function(i) {
list.push(function () { return i; });
};
for (var i = 0; i < 5; i++) {
_loop_1(i);
}
list.forEach(function (f) { return console.log(f()); });
結果は次のようになります。
0
1
2
3
4
for..in文チェックの改善
以前は、for..in
の変数の型をany
と推論していたため、
for..in
の本文内での不正な使われ方がコンパイラによって許可されていました。
TypeScript 1.8からは、
-
for..in
文の中で定義された変数の型は、暗黙的に文字列となります。 -
型
T
の数値インデックスのシグネチャを持つオブジェクト(配列のような)が、 文字列のインデックスのシグネチャではなく、数値のインデックスのシグネチャを持つオブジェクトのために、for..in
文に含まれるfor..in
の変数によってインデックスされる場合(再度配列のように)、 型T
の値が作られます。
var a: MyObject[];
for (var x in a) { // xの型は暗黙的に文字列になります
var obj = a[x]; // objの型はMyObjectです
}
モジュールによる"use strict"の文頭出力
モジュールはES6を対象とする場合には常にstrictモードで解析されていましたが、 非ES6を対象とする場合は生成されたコード内でこれが尊重されていませんでした。 TypeScript 1.8からは、生成されたモジュールは常にstrictモードになります。
これはTSがほとんどのstrictモードのエラーをコンパイル時のエラーとみなすため、
ほぼ全てのコードで目に見える変化を確認することは出来ないでしょう。
ただし、NaN
の割り当てのような、TSコードで内でランタイム時に沈黙的に失敗する幾つかの事が、
騒々しく失敗するようになります。
strictモードと非strictモードの違いの詳細なリストを、 MDNの記事で参照することができます。
--allowJsに.jsファイルを含める
プロジェクトには、TypeScriptで作成できない外部ソースファイルが存在することがよくあります。
あるいは、JSベースのコードをTSに変換しようとしているものの、 新しいTSコードの出力と一緒に、全てのJSコードを単一のファイルにまとめたいというニーズがあるかもしれません。(翻訳に自信なし)
.js
ファイルは、tsc
への入力対象として許可されるようになりました。
TypeScriptコンパイラは入力対象の.js
ファイルの構文エラーをチェックし、
--target
と--module
フラグを基にして正しく出力処理を実行します。
出力は他の.ts
ファイルと組み合わせることもできます。
ソースマップは.ts
ファイルの場合と同様に、.js
ファイル用に生成されます。
--reactNamespaceを使用したカスタムJSXファクトリー
--jsx react
と一緒に--reactNamespace <JSX factory Name>
を渡すことで、
そのデフォルトのReact
とは異なるJSXファクトリーが使用できるようになります。
新しいファクトリー名は、createElement
と__spread
関数の呼び出しに使用されます。
import {jsxFactory} from "jsxFactory";
var div = <div>Hello JSX!</div>
これを次のようにコンパイルすると、
tsc --jsx react --reactNamespace jsxFactory --m commonJS
結果は次のようになります。
"use strict";
var jsxFactory_1 = require("jsxFactory");
var div = jsxFactory_1.jsxFactory.createElement("div", null, "Hello JSX!");
thisを基にした型の保護
TypeScript 1.8は、ユーザー定義の型の保護(type guard)関数を、クラスとインターフェースメソッドへ拡張できるようになります。
this is T
はクラスとインターフェース内のメソッドで、正当な戻り値の型アノテーションになります。
型が絞り込まれる位置(if
文など)で使用されると、呼び出し式の対象オブジェクトの型がT
に絞り込まれます。
class FileSystemObject {
isFile(): this is File { return this instanceof File; }
isDirectory(): this is Directory { return this instanceof Directory;}
isNetworked(): this is (Networked & this) { return this.networked; }
constructor(public path: string, private networked: boolean) {}
}
class File extends FileSystemObject {
constructor(path: string, public content: string) { super(path, false); }
}
class Directory extends FileSystemObject {
children: FileSystemObject[];
}
interface Networked {
host: string;
}
let fso: FileSystemObject = new File("foo/bar.txt", "foo");
if (fso.isFile()) {
fso.content; // fso is File
}
else if (fso.isDirectory()) {
fso.children; // fso is Directory
}
else if (fso.isNetworked()) {
fso.host; // fso is networked
}
公式のTypeSriptのNuGetパッケージ
TypScript 1.8からは、Typescriptコンパイラ(tsc.exe
)とMSBuildインテグレーション(Microsoft.TypeScript.targets
およびMicrosoft.TypeScript.Tasks.dll
)で、
公式のNuGetパッケージが使用できるようになりました。
Stableのパッケージで使用できるものは下記のとおりです。
また、nightlyのnpmパッケージと一致するnightlyのNuGetパッケージは、 mygetで利用できます。
tscのエラーメッセージの装飾
我々は、大量のモノクロ出力が目に優しくないことは重々承知しています。 色を付けることでメッセージの開始と終了の識別を容易にし、 大量のエラー出力があった場合に、この視覚的な手がかりが重要になってきます。
--pretty
コマンドラインオプションを渡すだけで、
TypeScriptはどこに誤りがあるのかを色付きで教えてくれます。
Visual Studio 2015によるJSXコードの色付け
TypeScript 1.8では、Visual Studio 2015でJSXタグが、分類・色付されるようになりました。
この分類は、Tools
->Options
->Environment
->Fonts and Colors
のページで、
VB XML色とフォント設定の、フォントと色の設定を変更することで更にカスタマイズすることができます。
--project (-p)フラグのファイルパスの受け入れ
--project
コマンドラインオプションは、もともとtsconfig.json
を含むフォルダへのパスしか取れませんでした。
ビルド設定に異なるシナリオを与えると、--project
は他の互換性のあるJSONファイルを指すことができるようになりました。
例えば、ユーザーがNode5のCommonJSモジュールではES2015をターゲットにしたいが、
ブラウザ用のAMDモジュールではES5をターゲットにしたいケースがあるかもしれません。
この新しい動作により、tsconfig.json
ファイルを別のディレクトリに配置するといった回避策やハックを行うことなく、
tsc
だけを使用して2つの別々のビルドターゲットを簡単に管理できるようになりました。
ディレクトリが指定されても、元の古い動作は変わりません。
コンパイラは、tsconfig.json
という名前のファイルをそのディレクトリ内で見つけようとします。
tsconfig.json内へのコメント
設定にドキュメントを残せるということは、どんな場合でも歓迎されることでしょう。
tsconfig.json
に、単一行・複数行のコメントが記述できるようになりました。
{
"compilerOptions": {
"target": "ES2015", // running on node v5, yaay!
"sourceMap": true // makes debugging easier
},
/*
* Excluded files
*/
"exclude": [
"file.d.ts"
]
}
IPC-drivenファイルへの出力のサポート
TypeScript 1.8では、ユーザーが--outFile
引数を、
名前付きパイプやデバイスなどの特殊なファイルシステムのエンティティで使用できるようになりました。
一例として、多くのUnixライクなシステムでは、標準出力のストリームは/dev/stdout
ファイルからアクセスできます。
tsc foo.ts --outFile /dev/stdout
これはコマンド間で、出力をパイプするのにも使用することができます。
例えば、生成したJavaSccriptをpretty-jsのようなプログラム整形器(pretty printer)にパイプすることができます。
tsc foo.ts --outFile /dev/stdout | pretty-js
Visual Studio 2015のtsconfig.jsonサポートの改善
TypeScript 1.8では、tsconfig.json
ファイルが全てのタイプのプロジェクトで使用できるようになります。
これには、TypeScriptを使用したASP.NET v4プロジェクト、コンソールアプリケーション、HTMLアプリケーションのプロジェクトのタイプが含まれます。
更にひとつのtsconfig.json
ファイルに縛られることなく、
複数のファイルを追加して、各プロジェクト毎にそれぞれのファイルを構築することが可能です。
これにより、複数の異なるプロジェクトを持つこと無く、アプリケーションのさまざまな部分の構成を分離することが可能になります。
また、tsconfig.jsonファイルが追加されると、プロジェクトのプロパティページを無効にするようにしました。
これは、構成の変更が必要であればtsconfig.json
ファイル自体にそれを行う必要があることを意味します。
いくつかの制限
-
tsconfig.json
ファイルを追加すると、そのコンテキストの一部とみなされないTypeScriptファイルはコンパイルされません。 -
Apache Cordovaアプリケーションには、依然としてルートまたは
scripts
フォルダーのいずれかに、 単一のtsconfig.jsonファイルを配置しなければいけない制限があります。 -
ほとんどのプロジェクトのタイプに、
tsconfig.json
のためのテンプレートが存在しません。
© https://github.com/Microsoft/TypeScript-Handbook
このページは、ページトップのリンク先のTypeScript-Handbook内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。
- ドキュメントの情報が古い可能性があります。
- "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。