TypeScript 2.0
- nullとundefinedを認識する型
- 制御フローベースの型分析
- タグ付き共用体型(Tagged union types)
- never型
- 読み取り専用プロパティとインデックスシグネチャ
- 関数のためのthisの型の指定
- tsconfig.jsonでのglobサポート
- BaseUrl、パスマッピング、rootDirs、追跡によるモジュール解決機能の向上
- 簡略(shorthand)アンビエントモジュール宣言
- モジュール名でのワイルドカード文字
- UMDモジュール定義のサポート
- 任意のクラスプロパティ
- privateとprotectedのコンストラクタ
- 抽象プロパティと抽象アクセサ
- 暗黙のインデックスシグネチャ
- --libを使用した組み込み型宣言の読み込み
- --noUnusedParametersと--noUnusedLocalsを使用した未使用の宣言へのフラグ
- モジュール識別子による.js拡張子の許可
- 'target : es5'と'module: es6'の組み合わせのサポート
- 関数パラメータと引数リストのトレイリング・カンマ
- 新コンパイラオプション --skipLibCheck
- 宣言を跨いだ重複識別子の許可
- 新コンパイラオプション --declarationDir
nullとundefinedを認識する型
TypeScriptにはヌル(Null)と未定義(Undefined)という2つの特別な型があり、
それぞれnull
とundefined
の値を持ちます。
以前はこれらの型を明示的に指定することはできませんでしたが、
型検査のモードに関係なく、型名としてnull
とundefined
を使用できるようになりました。
以前は、型チェッカーはnull
とundefined
を、どんなものにも割り当て可能なものとして扱っていました。
実際には、null
とundefined
は全ての型で有効な値であり、明確に除外することはできませんでした。
(そのため、誤った使用を検知することができませんでした)
--strictNullChecks
--strictNullChecks
を指定すると、新しい厳格(strict)なnull検査モードに切り替わります。
厳格なnull検査モードでは、null
とundefined
値は全ての型の領域から外れ、
自分自身とany
にしか割り当てることができません。
(1つ例外があり、undefined
はvoid
に対して割当可能です。)
そのため、通常の型検査モードでT
とT | undefined
が同義であるとみなされる一方で(undefined
は、あらゆるT
の部分型(subtype)とみなされるため)、
厳格な型検査モードでは異なる型と判定され、T | undefined
でのみundefined
値が許可されます。
T
とT | 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
への型の保護は、x
、x.y
、x.y.z
への割り当ての後は作用しなくなります。
式演算子
式演算子には、オペランド型にnull
および/またはundefined
を含めることができますが、
必ずnull
、undefined
以外の型の値を生成します。
// Compiled with --strictNullChecks
function sum(a: number | null, b: number | null) {
return a + b; // number型の値を生成
}
&&
演算子は、左オペランドの型が提供されているかどうかに応じて、
右オペランドの型に対してnull
および/またはundefined
が追加され、
||
演算子は、共用体型の結果に応じて、左オペランドの型からnull
とundefined
の両方を削除します。
// 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
と推論されます。
(そのため、型の注釈がない場合はnull
がz
の唯一の値になります。)
非null(Non-null)アサーション演算子
新しい後ろに付加する(post-fix)式演算子である!
は、
型チェッカーが断定することが出来ない状況で、そのオペランドが非null、非undefinedであることを主張することに使用できます。
具体的には演算子x!
は、x
の型の値をnull
とundefined
を除外したもので生成します。
<T>x
とx 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
文と?:
条件式に限定されて実行されていて、
代入やreturn
、break
文のような制御フローの構成は考慮されていませんでした。
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 == v
、x.p === v
、x.p != v
、x.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: void
はaddClickListener
が、onclick
がthis
の型を必要としない関数であることを期待することを意味します。
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
)ファイルを含めます。
allowJs
がtrue
に設定されている場合は、JSファイル(.js
および.jsx
)も含まれます。
"files"
または"include"
プロパティが指定されている場合は、
コンパイラはそれらの2つのプロパティに含まれるファイルの和集合を代わりに含めるようにします。
"outDir"
のコンパイラオプションを使用して指定されたディレクトリ内のファイルは、
"files"
プロパティによって明示的に指定されていない限り("exclude"
プロパティが指定されていても)、常に除外されます。
"include"
を使用して含まれるファイルは、"exclude"
プロパティを使用してフィルタリングできます。
ただし、"files"
プロパティを使用して明示的に含まれるファイルは、"exclude"
に関係なく常に含まれます。
"exclude"
プロパティは何も指定されない場合は、
node_modules
、bower_components
、jspm_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)宣言を使用してすぐにモジュールを使用することができるようになりました。
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モジュールとして知られています。 これらのライブラリは、インポートまたはグローバル変数のどちらかでアクセスすることができます。
例えば、
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宣言のグループの一覧を選択できるようになりました。
例えば、もしMap
、Set
、Promise
がランタイム時にサポートされることが期待できる場合、
--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: es5
とmodule: 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ファイルとは異なる場所に宣言ファイルを生成できます。
© https://github.com/Microsoft/TypeScript-Handbook
このページは、ページトップのリンク先のTypeScript-Handbook内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。
- ドキュメントの情報が古い可能性があります。
- "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。