TypeScriptとは
TypeScriptはMicrosoft社が開発したプログラミング言語です。
JavaScriptの文法をベースに、型注釈をつけるための特別な文法で拡張されているのが、大きな特徴です。裏を返せば、型注釈に関する文法以外の大部分がJavaScriptの文法でできているので、JavaScriptが書ける人は少しだけ学習コストをかければTypeScriptを習得できることになります。
拡張子は基本的に.tsですが、ReactなどでJSXを記述する場合だけ.tsxになります。また、tscというコマンドで静的型チェックやコンパイルを行います。
型注釈の基本
型注釈の付与は、リスト2で紹介した通り、変数や引数、関数などに型注釈を明示して、tscコマンドによる型チェックの恩恵を受けられるようにする機能です。基本的な注釈の仕方は、図2の2つのパターンを覚えておけば良いでしょう。
変数宣言に型注釈をすると、右辺と左辺の型に齟齬がないかをチェックできるようになります。関数定義に型注釈をすると、呼び出し側で引数に渡した値の型との齟齬がないかをチェックできるほか、戻り値の型を別の式でのチェックに利用できるようになります。また、TypeScriptには型推論という機能があり、右辺や戻り値から型が推測できるときには、型宣言が省略できる場合があります。リスト3でいくつかの実例を見てみましょう。
// 問題ない
const numberValue: number = 1;
// 型に齟齬があるためエラー
const numberValue2: number = '1';
// 右辺がstring型なので、左辺の型注釈を省略してもstringValueはstring型として扱われる
const stringValue = 'abc';
// (1) 戻り値がstring型なので、戻り値の型注釈を省略しても戻り値の型はstring型として扱われる
function func(count: number) {
return `現在のカウントは${count}です`;
}
// 左辺の型注釈と、右辺の戻り値型に齟齬がないかチェックされる
const message: string = func(numberValue);
// (2) 右辺の戻り値がstring型であることが分かっているので、左辺の型注釈は省略しても良い
const message2 = func(numberValue);
// 引数の型に齟齬があるためエラーとなる
const message3 = func(stringValue);
(1)や(2)のパターンでは、引数以外の型注釈を省略できました。型を省略できるケースは多いので、明示的にチェックしたい場所以外は、可能な限り省略しておくと良いでしょう。
型の種類
さて、ここまでの解説でstringやnumberといった型を取り上げてきましたが、どんな型が使えるのかを確認しておきましょう。まず、表1にある通り、JavaScriptのtypeofで確認できる型と同じものは利用することができます。
| 型名 | 意味 |
|---|---|
| string | 文字列型 |
| number | 数値型 |
| boolean | 真偽値型 |
| undefined | 未定義の値の型 |
| functon | 関数型 |
| object | オブジェクト型 |
これらに加えて、柔軟な型定義を実現できるように、表2のTypeScript独自の型も用意されています。
| 型名 | 意味 |
|---|---|
| any | 何でも型 |
| { a: number } | オブジェクトリテラル型 |
| (a: number) => number | 関数型 |
特によく使うものをピックアップしました。それぞれの例はリスト4の通りです。
// (1) anyはどんな値でも受け付ける
let anyValue: any = 1;
anyValue = 'abc'; // エラーにならない
// (2) オブジェクトリテラル型はオブジェクトの構造を定義する
const person: { name: string, age: number } = {
name: '太郎',
age: 24,
};
// (3) 関数の引数と戻り値の型を定義するための型表現
function func(callback: (id: number) => void) {
callback(100);
}
(1)のany型は、型を明確に定義できないことを明示する場合に使用する型です。正しい型を定義する労力がかけられない場合に利用します。(2)のオブジェクトリテラル型は、object型をより詳細に表現するためのもので、オブジェクトの階層構造をオブジェクトと似た記法で定義する、複合的な型です。リスト4の例では{ name: string, age: number }が全体で1つの型として扱われます。(3)の関数型はfunction型をより詳細に表現するためのもので、アロー関数に似た記法で定義します。voidは「戻り値がない」ことを明示するための特殊な型です。
型に名前をつける
オブジェクトリテラルを使うことで、複雑な階層のオブジェクトでも、どんなプロパティ名にどんな型の値がひもづいているのかを、直感的に表現できます。しかし、いちいち{ name: string, age: number }の型を書くのは面倒ですね。
TypeScriptには、型に名前をつけて、新しい型として定義するための文法があります。typeとinterfaceの2つがありますが、できることにほとんど違いがないため、本記事ではtypeについて解説します。type記法は変数宣言と似た記法で、左辺に型の名前、右辺に元となる型を記述します(図3)。
このPerson型を使うと、前述のリスト4の(2)はリスト5の通りに書き換えられます。
type Person = { name: string, age: number };
const person: Person = {
name: '太郎',
age: 24,
};
こうして生み出した新しい型名は、通常のオブジェクトと同じようにimportやexportすることができます。アプリケーション内で共通で扱いたいデータ型があれば、ファイルやフォルダで整理しておくと、複数人での開発がはかどることでしょう。
ライブラリの型情報
さて、自分のプロジェクトで型を活用する方法については分かってきましたが、ここで気になるのは、よそから持ってきたライブラリの型はどうするのかというところです。幸い、TypeScriptにはJavaScript製のライブラリに型情報を添えて配布するための仕組みが整っています。型情報は.d.tsという拡張子で定義され、JavaScriptファイルと合わせて読み込むことで、自分のプロジェクトでTypeScriptのコードを扱うのと同じように、型の恩恵を受けることができます。
ライブラリの型情報を利用する方法は、ライブラリがTypeScriptをサポートしているかどうかで変わってきます。ライブラリが公式にTypeScriptをサポートしている場合は、ライブラリに.d.tsファイルが同梱されるので、npm installが完了した段階でnode_modulesに型情報がインストールされます。この場合は、特に追加の作業をせずに、型情報を利用することができます。
公式サポートがない場合でも、DefinitelyTypedと呼ばれるプロジェクトでサードパーティの型情報が提供されていれば、これを利用できます。@types/ライブラリ名の名前で配布されているパッケージをnpm installすると、型情報が利用できるようになります。
型情報がまったく提供されていない場合は、.d.tsファイルを自作できますが、慣れるまでは型情報が用意されているライブラリを選んで使うと良いでしょう。
TypeScriptとBabelを併用する
元々のtscコマンドは「TypeScriptコードを任意のバージョンのJavaScriptに変換するためのコマンド」として生み出されました。しかし、BabelがJavaScriptコンパイラとしての地位を確立していくにつれて、「型チェックはTypeScriptでやりたいけれど、JavaScriptへの変換はBabelでやりたい」といった要求も増えていき、住み分けが難しくなっていきました。
この問題は、次の2つのアプローチによって解決されました。
-
tscコマンドに静的型チェックのみを行い、JavaScriptへの変換を行わないモードを設けた -
TypeScriptコードからTypeScript固有の表現を排除するためのBabelプラグイン集として
@babel/preset-typescriptを開発した
前述の通り、TypeScriptはほとんどJavaScriptの文法でできているため、@babel/preset-typescriptによって型注釈やtypeによる型定義を削除する前処理によって、通常のBabelで処理できるJavaScriptコードが生成できます。これにより、Babelによるコンパイルを行っていた多くのプロジェクトで、TypeScriptの導入が進むことになったのです。
