TypeScriptのnever型とは?使われる場面と意味を初心者向けにやさしく解説!
生徒
「先生、TypeScriptでnever型っていうのを見たんですが、これって何ですか?」
先生
「とてもいいところに気がつきましたね。never型は、ちょっと特殊な場面で使われる型なんですよ。」
生徒
「どういうときに登場するんですか?普通の変数には使わないんですよね?」
先生
「そのとおり!実はnever型は、値が絶対に返ってこない関数や、絶対に到達しないコードに使われるんです。詳しく見ていきましょう!」
1. never型とは?
never型は、TypeScriptの中でも少し特殊な型で、「絶対に値が返らない」ことを表します。つまり、関数の処理が途中で終わって戻ってこない場合や、絶対に実行されない場所などに使われます。
たとえば、次のような場面で登場します。
- 例外(エラー)を投げて、処理が止まる関数
- 無限ループなどで処理が止まらない関数
- 型チェックの中で「絶対にありえない」パターンを扱う場所
2. エラーを投げる関数に使われる例
エラーを発生させる関数は、実行中に処理が中断されるため、returnで値を返すことがありません。そのため、TypeScriptではこのような関数の戻り値の型としてneverが使われます。
function throwError(message: string): never {
throw new Error(message);
}
この関数はthrow(スロー)によってエラーを出して終了するので、呼び出し元に何も返しません。こうした関数はnever型と判断されます。
3. 無限ループで終了しない関数
もう一つの例として、永久にループし続ける関数もあります。これは関数から処理が戻ってこないため、never型になります。
function infiniteLoop(): never {
while (true) {
// ずっとループして終わらない
}
}
このような関数も、正常に終了して戻ることがないため、never型になります。
4. 型チェックでありえないケースを検出
TypeScriptでは、「絶対に発生しないはずの型」の処理を検出するために、never型が使われることもあります。これは型の安全性を高めるために使うテクニックです。
type Animal = "dog" | "cat";
function speak(animal: Animal) {
if (animal === "dog") {
console.log("ワンワン!");
} else if (animal === "cat") {
console.log("ニャーニャー!");
} else {
// ここは絶対に到達しないはずなのでnever型
const neverValue: never = animal;
}
}
このようなelse文は、本来なら実行されないはずです。
しかし、もしAnimal型に"bird"などの新しい値を追加したときに、ここに到達する可能性が出てきます。
never型を使っておくことで、「おかしい!」とTypeScriptが教えてくれるのです。
5. never型とvoid型の違い
ここでよく間違えやすいのが、void型との違いです。どちらも「戻り値がない」ときに見かける型ですが、意味がまったく異なります。
- void型:「何も返さない」関数(例:
console.log) - never型:「絶対に戻ってこない」関数(例:エラーを投げる)
つまり、voidは処理が最後まで終わるけど何も返さない。neverは最後までいかずに強制終了や無限ループで終わらない、という違いがあります。
6. never型は自分で使うより「登場する」
never型は、自分で積極的に使うことはあまりありません。
多くの場合、TypeScriptの型推論(かたすいろん)やエラーチェックの中で、自動的に出てくることが多いです。
つまり、「これは絶対に起こらないよね?」というチェックをしてくれる心強い型なのです。
まとめ
TypeScriptにおけるnever型は、一見すると使いどころがなさそうに思えるかもしれませんが、実は型の安全性を高めるための非常に重要な要素です。特に、例外処理や無限ループ、到達不能なコードの確認など、「通常のロジックが通らない場所」を明確にする役割があります。
初心者にとって最初は少し難解に感じるかもしれませんが、コードの流れや制御の漏れを防ぎ、堅牢なプログラムを書くうえで非常に心強い存在となります。たとえば、条件分岐の最後にnever型を使うことで、「本来起きてはならないケース」が発生したときに警告してくれるという利点があります。
また、void型と混同しやすい点にも注意が必要です。voidは「値を返さないが処理は終わる関数」に使い、neverは「値を返す以前に、そもそも戻ってこない関数」に使われるという明確な違いを理解しておくと、今後のTypeScript学習が一層スムーズになります。
次のようなコード例も、理解の助けになるでしょう。
function assertUnreachable(x: never): never {
throw new Error("想定外の値です: " + x);
}
type Fruit = "りんご" | "みかん";
function printFruit(fruit: Fruit) {
switch (fruit) {
case "りんご":
console.log("甘酸っぱいりんごです!");
break;
case "みかん":
console.log("さっぱりしたみかんです!");
break;
default:
assertUnreachable(fruit); // ここがnever型の出番!
}
}
このように、あらゆる選択肢を網羅したうえで、抜け漏れがないかを確認するためにnever型を使うことで、保守性や安全性の高いコードを書くことができます。
実際の開発現場では、複雑なユニオン型や列挙型(enum)と併用することで、将来の変更に対する堅牢なガードとして活用されます。
まとめると、never型は「意図しないパターンに出会ったときの最後の砦」ともいえる存在です。今は実際に手を動かして書く機会は少ないかもしれませんが、コードを読む力やswitch文の網羅性を高める意識の中で、自然とその役割が見えてくるでしょう。
生徒
「never型って、何も返さないってことじゃなくて、絶対に戻らない関数に使うってことなんですね!」
先生
「その通りです。たとえばthrowでエラーを出して処理が止まるとか、while(true)みたいに永久にループし続ける関数ですね。」
生徒
「あと、switch文で全部のケースを扱い終わったあと、defaultでneverを使うっていうのも、ちょっとカッコよかったです!」
先生
「あれはとても良い実践ですね。型が追加されたときにも気づけるので、プログラムのミスを未然に防げますよ。」
生徒
「なるほど、neverって自分ではあんまり書かないけど、出てきたときに意味を理解できるようにしておくのが大切なんですね。」
先生
「そうですね。今回学んだnever型の登場場面やvoid型との違いをしっかり覚えておくと、より正確で安全なコードが書けるようになりますよ。」