🧩
【初学者向け】TypeScriptで網羅性チェックを活用しよう
本記事では、TypeScript初学者に向けて、網羅性チェックの重要性とその具体的な実装方法を解説します。
網羅性チェックとは
網羅性チェック(Exhaustiveness Checking)とは、コード内で型のすべてのケースが適切に処理されていることをコンパイラが保証する仕組みです。
網羅性チェックの重要性
-
予期せぬバグの防止
- コードに未処理のケースが存在したときに、コンパイラが警告を出してくれる
-
保守性の向上
- 仕様変更時に未処理のケースがあれば、コンパイラの警告で気付くことができる
- 型のすべてのケースを明示的に扱うことで、コードの意図が分かりやすくなる
網羅性チェックが無い場合のリスク
以下の例では、網羅性チェックが無いためにケースが漏れている状態が発生しています。
type Status = 'success' | 'error' | 'loading'
function handleStatus(status: Status) {
if (status === 'success') {
console.log('成功')
} else if (status === 'error') {
console.log('エラー')
}
// "loading" のケースが考慮されていない
}
このコードでは"loading"
のケースの考慮が漏れており、意図しない挙動を引き起こす可能性があります。
網羅性チェックの実現方法
never
型を使ったチェック
TypeScriptでは、never
型を使用して網羅性チェックを行います。
never型について
本記事ではnever
型自体の解説は取り扱いません。詳しく知りたい方は以下の記事が分かりやすいのでおすすめです。
never
型は「値を持たない」を意味するTypeScriptの特別な型です。
以下の例では、"loading"
のケースを考慮していないため、default
節でエラーが発生します。
type Status = 'success' | 'error' | 'loading'
function handleStatus(status: Status) {
switch (status) {
case 'success':
console.log('成功')
break
case 'error':
console.log('エラー')
break
default:
assertNever(status)
// 型 'string' の引数を型 'never' のパラメーターに割り当てることはできません。
}
}
function assertNever(value: never): never {
throw new Error('Unexpected value: ' + value)
}
satisfies
演算子を使用した簡略化
TypeScript 4.4以降ではsatisfies
演算子を使うことで、網羅性のチェックを簡略化できます。
type Status = 'success' | 'error' | 'loading'
function handleStatus(status: Status) {
switch (status) {
case 'success':
console.log('成功')
break
case 'error':
console.log('エラー')
break
default:
throw new Error(status satisfies never)
// 型 'string' は想定された型 'never' を満たしていません。
}
}
Record
型を使ったチェック
switch
文を使用せず、Record
型を用いてパターンごとの処理を定義する方法もあります。
この方法では、全てのケースを定義しないとコンパイルエラーが発生します。
type Status = 'success' | 'error' | 'loading'
const handleStatus = (status: Status): void => {
// プロパティ 'loading' は型 '{ success: () => void; error: () => void; }' にありませんが、型 'Record<Status, () => void>' では必須です。
const handlers: Record<Status, () => void> = {
success: () => console.log('成功しました!'),
error: () => console.log('エラーが発生しました。'),
}
handlers[status]()
}
参考文献
Discussion