TypeScriptのnever型 — 3つの登場シーンと使い所をわかりやすく解説
TypeScriptを使っていると、ふとしたタイミングでコードの中に never という型が出てくることがあります。
neverが出てきたけど、なんでそこがnever型になってるのかわからない…
この記事では、never型がどういう型なのか・どこに自然に現れるのか・意図的にどう活用するのかを、順番に説明していきます。
この記事は次のような方におすすめです。
- TypeScriptを学んでいて、never型が気になりだした方
- コードの中でneverが出てきたが、何を意味するのかわからない方
- void型とnever型の違いをはっきりさせたい方
- ユニオン型の網羅性チェックについて知りたい方
この記事を読むと、never型の概念・実際に現れる場面・意図的に使うパターンが理解できます。型エラーのメッセージが読みやすくなる第一歩にもなるはずです。
それでは、順を追って詳しく見ていきましょう!
- 未経験で後悔したくない
【実体験】未経験からITエンジニアに転職して後悔した話|4社経験してわかった「最初の選択ミス」 - 年収が低くて不安
4年間ずっと年収260万だったエンジニアが、転職で510万になるまでの全記録
never型とは — 「絶対に値が返らない」を表す型
never型は、「値が存在しない」「絶対にそこには到達しない」ことを表す型です。
stringやnumberのように「何らかの値を持つ型」とは全く別物で、never型の変数には文字通り「何も入らない」のです。
具体的にどういうときに使うかというと、「その場所に到達したら、プログラムが正常に動くはずがない」という文脈で登場します。たとえば必ず例外を投げる関数や、絶対に終わらない無限ループ関数の戻り値の型がneverになります。
「値が存在しない型」というのは、集合でいうと「空集合」に近いイメージです。要素が一つもない集合、つまりどんな値も属さない型——それがneverです。
neverの2つの特性を理解する
neverには、直感に少し反する2つの特性があります。これを先に押さえておくと、後の話がスムーズに理解できます。
① never型の変数には、何も代入できない
let value: never;
value = 1; // エラー:Type 'number' is not assignable to type 'never'
value = "hello"; // エラー
value = null; // エラー
never型の変数には、stringでもnumberでもnullでも、何も入れることができません。「値が存在しない型」なので、何かを代入しようとするとTypeScriptが即座にエラーを出します。
② never型の値は、どんな型の変数にも代入できる
逆に、never型の値はstring型やnumber型の変数にも代入できます。
function fail(message: string): never {
throw new Error(message);
}
const s: string = fail("エラー"); // 型エラーにならない
const n: number = fail("エラー"); // 型エラーにならない
「それっておかしくない?」と感じるかもしれませんが、考えると筋が通っています。fail()は必ず例外を投げるので、実際にはsやnに値が入ることは絶対にありません。「到達しない場所での代入」は、どんな型に対しても問題ない——そういう扱いになっています。
never型はどこに現れるのか
never型を意図的に使う前に、まず「気づかずにneverが出てくるケース」を知っておきましょう。コードを読んでいてneverが現れたときに、何が起きているのかが読めるようになります。
必ず例外を投げる関数
関数の中にthrowだけがあって、正常な戻り値を返さない場合、TypeScriptはその関数の戻り値の型をneverと推論します。
function throwError(message: string): never {
throw new Error(message);
// この関数は絶対に値を返さない
}
この関数は必ず例外を投げて終わるため、呼び出し元に値が返ってくることは「絶対にない」。だから戻り値の型はneverです。
無限ループ関数
終わりのない無限ループを持つ関数も、同じ理由で戻り値がneverになります。
function infiniteLoop(): never {
while (true) {
// 永遠に繰り返す処理
}
}
「正常に終わって値を返す」ことが絶対にないので、戻り値の型はneverです。
型の絞り込みで消えたとき
もう少し高度なケースですが、知っておくと役立ちます。
ユニオン型を型ガードで絞り込んでいくと、最終的にすべての型の可能性がなくなった時点で、その箇所の型がneverになります。
type Shape = "circle" | "square";
function describe(shape: Shape): string {
if (shape === "circle") {
return "丸"; // ここのshapeは "circle"
}
if (shape === "square") {
return "四角"; // ここのshapeは "square"
}
// ここではすべての可能性が消えた → shapeはnever型
// 実際には絶対に到達しない
const _check: never = shape;
throw new Error("想定外の値");
}
この「絞り込んで消えた型がnever」という性質が、次に紹介する網羅性チェックの核心です。
never型を意図的に使う — ユニオン型の網羅性チェック
ここからが、never型を積極的に活用する場面です。
ユニオン型に対してswitch文で各ケースを処理するパターンはよく出てきます。このときneverを組み合わせると、「ケースの処理漏れを型エラーとして検出できる」という仕組みが作れます。
switch文とneverの組み合わせ方
次のコードを見てください。
type Shape = "circle" | "square" | "triangle";
function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return 3.14;
case "square":
return 1;
default:
// ここに到達する = 処理が漏れているケースがある
const _exhaustiveCheck: never = shape; // 型エラーが出る!
throw new Error(`未対応の形: ${_exhaustiveCheck}`);
}
}
このコードは"triangle"のケースが抜けています。するとdefault節に"triangle"が流れ込んできます。このときshapeの型は"triangle"なので、never型の変数_exhaustiveCheckに代入しようとするとTypeScriptが型エラーを出してくれます。
これが「網羅性チェック(exhaustive check)」と呼ばれるパターンです。_exhaustiveCheckという変数名は慣例的によく使われます(名前はなんでもOKです)。
ケースを追加したときに型エラーで気づける効果
このパターンの本当のメリットは、後から型を追加したときに、処理の追加漏れをエラーで即座に気づけることです。
たとえば後からShapeに"pentagon"を追加したとします。
// "pentagon"を追加
type Shape = "circle" | "square" | "triangle" | "pentagon";
function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return 3.14;
case "square":
return 1;
case "triangle":
return 0.5;
default:
// "pentagon"がここに流れ込む → neverへの代入で型エラー!
const _exhaustiveCheck: never = shape;
throw new Error(`未対応の形: ${_exhaustiveCheck}`);
}
}
getArea関数に"pentagon"のcaseを追加し忘れていると、TypeScriptが型エラーで教えてくれます。
網羅性チェックがない場合、「pentagonが渡されたときに想定外の動作をする」というバグが実行時まで気づかれないことがあります。neverを使うことで、そのリスクをコンパイル時に防げます。
void型との違いを整理する
never型を学ぶと、「void型と何が違うの?」という疑問が出てきます。どちらも「関数が値を返さない」文脈で登場するため、混乱しやすいポイントです。
一言で言うと、voidは「返す値がないが、関数は正常に終わる」、neverは「関数が絶対に終わらない」という違いがあります。
| void | never | |
|---|---|---|
| 意味 | 返す値がない(undefinedを返す) | 絶対に値を返さない・到達しない |
| 使う場面 | 副作用のみの関数(console.logなど) | 例外を投げる関数・無限ループ |
| 正常終了 | する | しない |
// void: 正常に終わるが、返す値がない
function log(message: string): void {
console.log(message);
// undefinedが暗黙的に返る
}
// never: 正常には終わらない
function fail(message: string): never {
throw new Error(message);
// ここに到達することはない
}
「返す値がないだけで、関数自体は終わる」ならvoid。「そもそも関数が正常には終わらない」ならneverと覚えておきましょう。
【付録】さらに学びを深めるためのリソース
さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。
おすすめの書籍
ゼロからわかる TypeScript入門
技術評論社から出版されている「ゼロからわかる TypeScript入門」は、プログラミング初心者や本職プログラマーではない方を主な対象にした入門書です。
変数・条件分岐・ループといった基本から、クラスやインターフェース、モジュールまで段階的に学べる構成になっています。最終章ではWeb APIとJSONを使った非同期Webアプリの作成も体験できるので、「実際に動くものを作る」ところまで到達できます。
プロを目指す人のためのTypeScript入門
技術評論社の「プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで」、通称 ブルーベリー本 です。
JavaScriptの仕様とTypeScript独自の機能を両方押さえつつ、リテラル型・ユニオン型・keyof型・ジェネリクスなど、高度な型表現まで踏み込んで解説しています。TypeScriptの型システムの表現力を本格的に学べる一冊です。
オンラインで参照できる公式ドキュメント
TypeScript公式ハンドブック
https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。
TypeScript Deep Dive
https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。
TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。
まとめ — never型を知ると「型エラーの意味」が見えてくる
この記事で扱ったことを振り返ります。
- never型は「値が存在しない・絶対に到達しない」を表す型
- neverの2つの特性:何も代入できない/逆に何にでも代入できる
- 自然にneverが現れる場面:例外を投げる関数・無限ループ・型の絞り込みで消えたとき
- 意図的な使いどころ:ユニオン型のswitch文と組み合わせた網羅性チェック
- void型との違い:voidは正常終了する・neverはしない
never型は最初「なぜ必要?」と感じる型ですが、網羅性チェックのパターンを一度使うと、「型システムがバグを防いでくれている」という感覚が実感できると思います。コードが増えてきたときほど、この仕組みのありがたさがわかってきます。
※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。




