TypeScript の型システムの主な特徴については、「なぜ TypeScript なのか」の回で説明したとおりです。
-
TypeScript の型システムは、あなたの JavaScript が TypeScript になるように、オプションで設計されています。
-
TypeScript は Type Error があっても JavaScript の emit をブロックしないので、JS を徐々に TS に更新することができます。
では、まず TypeScript タイプシステムの文法について説明します。 こうすることで、これらのアノテーションをコードですぐに使い始めることができ、その効果を実感することができます。
前述のように、型は :TypeAnnotation
構文を使用してアノテーションされます。 型宣言スペースで利用可能なものはすべて、型アノテーションとして使用できます。
次の例では、変数、関数パラメーター、および関数の戻り値に対する型アノテーションを示しています。
var num: number;var str: string;var bool: boolean;num = 123;num = 123.456;num = '123'; // Errorstr = '123';str = 123; // Errorbool = true;bool = false;bool = 'false'; // Error
配列
TypeScript は配列のための専用の型構文を提供し、コードの注釈や文書化を容易にする。 この構文は基本的に、有効な型注釈 (例: :boolean
) に をポストフィックスするものです。 これにより、通常行うような配列操作を安全に行うことができ、間違った型のメンバを代入してしまうようなエラーから保護されます。
var boolArray: boolean;boolArray = ;console.log(boolArray); // trueconsole.log(boolArray.length); // 2boolArray = true;boolArray = ;boolArray = 'false'; // Error!boolArray = 'false'; // Error!boolArray = ; // Error!
Interfaces
Interfaces は、複数の型アノテーションを単一の名前付きアノテーションに合成する TypeScript のコアな方法である。 次の例を見てみましょう。
interface Name {first: string;second: string;}var name: Name;name = {first: 'John',second: 'Doe'};name = { // Error : `second` is missingfirst: 'John'};name = { // Error : `second` is the wrong typefirst: 'John',second: 1337};
ここで、first: string
+ second: string
のアノテーションを、個々のメンバーに対して型チェックを行う新しいアノテーション Name
に合成しています。
Inline Type Annotation
新しい interface
を作成する代わりに、:{ /*Structure*/ }
を使用してインラインで好きなものにアノテーションを付けることができる。 前の例では、再びインライン タイプを示しました:
var name: {first: string;second: string;};name = {first: 'John',second: 'Doe'};name = { // Error : `second` is missingfirst: 'John'};name = { // Error : `second` is the wrong typefirst: 'John',second: 1337};
インライン タイプは、何かに対して 1 回限りのタイプ注釈をすばやく提供するのに適しています。 それは、(潜在的に悪い)型名を考え出す手間を省くことができます。 しかし、同じ型注釈を何度もインラインに置いていることに気づいたら、それをインターフェイス (またはこのセクションで後述する type alias
) にリファクタリングすることを検討するのがよいでしょう。 any
, null
, undefined
, void
である。
any
any
型はTypeScriptの型システムで特別な位置を占めている。 この型はコンパイラに立ち去るように伝えるための型システムからの逃げ道を提供する。 any
は型システムのあらゆる型と互換性があります。 つまり、どんなものにも代入でき、どんなものにも代入できるのです。
var power: any;// Takes any and all typespower = '123';power = 123;// Is compatible with all typesvar num: number;power = num;num = power;
JavaScriptのコードをTypeScriptに移植する場合、最初のうちはany
と仲良しになることだろう。 しかし、この友情は、型安全性を確保するのはあなた次第であることを意味するので、あまり深刻に考えないでください。
null と undefined
これらが型システムでどのように扱われるかは、strictNullChecks
コンパイラフラグに依存します (このフラグについては後で説明します)。 strictNullCheck:false
の場合、null
と undefined
の JavaScript リテラルは事実上 any
型の何かと同じように型システムで扱われます。 これらのリテラルは他の型に割り当てることができる。 これは以下の例で示されています:
var num: number;var str: string;// These literals can be assigned to anythingnum = null;str = undefined;
:void
関数が戻り値の型を持たないことを示すために :void
を使用します:
function log(message): void {console.log(message);}
Generics
コンピュータサイエンスの多くのアルゴリズムやデータ構造では、実際のオブジェクト型には依存しないものがあります。 しかし、それでも様々な変数間の制約を強制したいものです。 簡単なおもちゃの例としては、項目のリストを受け取り、項目のリストを反転して返す関数があります。
function reverse<T>(items: T): T {var toreturn = ;for (let i = items.length - 1; i >= 0; i--) {toreturn.push(items);}return toreturn;}var sample = ;var reversed = reverse(sample);console.log(reversed); // 3,2,1// Safety!reversed = '1'; // Error!reversed = ; // Error!reversed = 1; // Okayreversed = ; // Okay
ここであなたは基本的に、関数 reverse
がある型 T
の配列 (items: T
) を受け取り(reverse<T>
の型パラメータに注目)、型 T
の配列を返す(: T
に注目)、ということを意味しています。 reverse
関数は受け取ったものと同じ型のアイテムを返すので、TypeScriptはreversed
変数がnumber
型であることも知っており、Type safetyを与えてくれるのである。 同様に、逆関数にstring
の配列を渡すと、返される結果もstring
の配列となり、以下のような型安全性が得られる:
var strArr = ;var reversedStrs = reverse(strArr);reversedStrs = ; // Error!
実はJavaScriptの配列にはすでに.reverse
関数があり、TypeScriptはその構造を定義するのに実際にジェネリックスを使っているのである。
interface Array<T> {reverse(): T;// ...}
つまり、以下のように任意の配列に対して.reverse
を呼び出すと、型安全性が得られるということです:
var numArr = ;var reversedNums = numArr.reverse();reversedNums = ; // Error!
Array<T>
インタフェースについては、後ほど「環境宣言」でlib.d.ts
について詳しく説明する予定です。
ユニオン型
JavaScript ではごく一般的に、あるプロパティが複数の型のいずれかになるようにしたい場合があります(例:string
や number
など)。 そこで、ユニオン型(string|number
のような型注釈で|
で示される)が便利になります。 一般的な使用例としては、単一のオブジェクトまたはオブジェクトの配列を受け取ることができる関数があります。
function extend<T, U>(first: T, second: U): T & U {return { ...first, ...second };}const x = extend({ a: "hello" }, { b: 42 });// x now has both `a` and `b`const a = x.a;const b = x.b;
Tuple Type
JavaScript にはファーストクラスのタプルのサポートがありません。 人々は一般的に、タプルとして配列を使用します。 これはまさに TypeScript の型システムでサポートされているものです。 タプルは:
などのアノテーションを使用できます。 タプルは任意の数のメンバーを持つことができる。
var nameNumber: ;// OkaynameNumber = ;// Error!nameNumber = ;
TypeScriptの構造改革のサポートと組み合わせると、タプルはその下が配列であるにもかかわらず、かなりファーストクラスのように感じられます:
var nameNumber: ;nameNumber = ;var = nameNumber;
Type Alias
TypeScriptには、複数の場所で使用したい型注釈に名前を付けるための便利な構文があります。 エイリアスは type SomeName = someValidTypeAnnotation
構文を使用して作成されます。
type StrOrNum = string|number;// Usage: just like any other notationvar sample: StrOrNum;sample = 123;sample = '123';// Just checkingsample = true; // Error!
interface
とは異なり、文字通り任意の型注釈に型エイリアスを与えることができます (論理和や交差型のようなものに便利です)。
type Text = string | { text: string };type Coordinates = ;type Callback = (data: string) => void;
TIP: Type 注釈の階層を持つ必要がある場合、
interface
を使用します。 これらはimplements
とextends
で使用できます。
TIP: 単純なオブジェクト構造 (
Coordinates
など) にセマンティックな名前を付けるために、型の別名を使用します。
まとめ
JavaScript コードのほとんどにアノテーションを付けることができたので、次は TypeScript の Type System で利用できるすべてのパワーの細部に飛び込んでいきましょう。