みなさんは「型」についてご存知でしょうか?「intとかcharみたいなアレでしょ?」くらいの知識はあるかと思います。今回の記事では型についての種々の疑問について解説して行きたいと思います。
- そもそも型とは?
- 型があるとなんで嬉しいの?
- 静的型付け・動的型付けって?
- プログラムに型がなくても静的型付けってどういうこと?
なお、この記事ではTypeScriptの型をもとに話しますが、他の言語でも言える普遍的な内容になっています。
そもそも型とは?
型とは、データの分類の名称のことです。たとえば0
、42
はnumber型
、''
、'hello world'
はstring型
です。
型はプリミティブ値以外の式にもつきます。たとえば式1 + 2
はnumber型
、式'hello' + ' ' + 'world'
はstring型
です。
また、変数や関数の引数・返り値にも型をつけることができます。型がついた変数は、その型の値しか代入できません。
たとえば、number型の値を2つ受け取ってnumber型の値を返すmax関数はこのようにかけます。
const max = (a: number, b: number): number => a > b ? a : b;
型があると嬉しいこと
型があると嬉しいことはいくつか挙げられます。
- 型注釈をみることで関数の動作をだいたい想像できる
- 静的型検査を行うことで「実行時エラー」をなくすことができる
1つ目はオマケで、2つ目のほうが重要です。
型注釈から関数の動作を予想できる
たとえば、map関数は以下のような型を持ちます。
const map = <T, T2>(f: (bef: T) => T2, a: T[]): T2[] => { const res = []; for (const item of a) res.push(f(item)); return res; }
fとaを受け取り、aの各要素にfを適用した結果を返す関数になっており、型定義もそのようになっています。
静的型検査を行って実行時エラーをなくせる
静的型検査というのは、プログラムを実行せずに型が正しくつけられているか検証する作業です。たとえば次のコードはTypeScriptの型検査ではじかれます。
const n = '3'; // おや? const m = 4; console.log(n * m); // 文字列と掛け算してしまっている!
実はJavaScriptではこのコードは意図したとおり(?)12を表示してくれます。これは静的型検査がないかわりに実行時に必要に応じてキャストしているためです。
しかし、もしnが外部からの入力だったとしたらどうでしょうか…?
const n = await readFile('hoge.txt', 'utf8'); // hoge.txtから読み込んでいるけど… const m = 4; console.log(n * m); // 数値で掛け算できている保証はない
これはごくカンタンな例でしたが、もっと巨大なコードになっていくと、いちいち調べるのは大変です。そこで、型をつけて検査をしてエラーをはじきます。
型検査
静的型付け・動的型付けとは?
- 静的型付けはプログラムを実行する前に型をつける
- 動的型付けはプログラム実行時に型をつける
TypeScriptは静的型付けをするので、静的型付き言語です。TS→JSのコンパイル時に型チェックが行われます。コンパイルが通れば実行時エラーが起きないことが保証されます*1。
型推論
静的型付き言語であっても、必ずしもすべてに型を書く必要はありません。たとえば目視でも「1 + 2
はnumber型
だ」という"推論"ができます。二項演算子なら、その左辺と右辺の項、演算子の情報さえあれば推論できます。この推論機能によって、型アノテーションを省略することができます。ただし型推論の目的は型アノーテーションの省力化ではありません。
もし型推論がなかったら、次のように書かなければなりません(下のような構文はそもそもTypeScriptにありませんが)。
const a : number = ((1 + 2 : number) * (3 - 4 : number) : number)
さすがにこれは過剰です。演算子は型が言語仕様から決まりきっているので、オペランドとの組み合わせが決まれば返り値の型は一意に決められます。型アノテーションが必要になることはありません。
関数の推論についてはややこしいので、参照記事をご覧ください。
ここで重要なのは、型推論は静的に行われることです。静的型検査を行いながら推論をしていくので、型推論によって不要な型アノーテーションを消したからといっても実行時エラーは起きえません。型推論と動的型付けはごっちゃになりがちなので、気をつけてください。
参照
*1:ただし、TypeScriptはJavaScriptのスーパーセットで、型がないところは勝手にany型がつけられて検査時にエラーが出ないため、適切な型付けをしていることが前提ですが