TL;DR
内容
進化する型とは
TypeScript の型は、一般に絞り込まれる方向にしか変わらない。しかし、null や undefined、空配列で初期化した変数の型は、変化することがある。このような型を「進化する型」と呼ぶ。
function range(start: number, limit: number) {
const nums = []
// ^? const nums: any[]
for (let i = start; i < limit; i++) {
nums.push(i)
// ^? const nums: any[]
}
return nums
// ^? const nums: number[]
}条件分岐では、分岐ごとに型が変わる。
let value
// ^? let value: any
if (Math.random() < 0.5) {
value = /hello/
value
// ^? let value: RegExp
} else {
value = 12
value
// ^? let value: number
}
value
// ^? let value: number | RegExp型が進化するのは、値を代入したり要素を追加したりした後である。進化する型を使うと型アノテーションを減らせる。また、この仕組みを知っておくと、このパターンを使ったコードを読むときに役立つ。
注意点
進化する型が any になるのは書き込むときだけである。読み込むときは暗黙の anyとして型エラーになる。
function range(start: number, limit: number) {
const nums = []
// Variable 'nums' implicitly has type 'any[]' in some
// locations where its type cannot be determined.
if (start === limit) {
return nums
// Variable 'nums' implicitly has an 'any[]' type.
}
// ...
}暗黙の any は、関数呼び出しを通じては進化しない。
function makeSquares(start: number, limit: number) {
const nums = []
// Variable 'nums' implicitly has type 'any[]' in some
// locations where its type cannot be determined.
range(start, limit).forEach((i) => nums.push(i))
return nums
// Variable 'nums' implicitly has an 'any[]' type.
}明示的に型アノテーションを付けたほうが、意図しないエラーを検出できる。
訳者による解説
本書は進化する型を推奨している。一方、同じ訳者による『初めての TypeScript』では、型チェッカが部分的に働かず、予期しない値が入る可能性があるので、このパターンを非推奨としている。通常の any と同じく好ましくない(項目5)。訳者自身は非推奨の立場である。
キーワード
- TypeScript
- 進化する型
- 暗黙の any
- 型アノテーション
自分の理解
自分も訳者と同じく、進化する型は使いたくない。any として推論されるということは IDE の補完も効かないはずなので、どのスコープであっても any が発生するのは極力避けたい。