TypeScript - Never: ボトムタイプの理解

こんにちは、未来のプログラマーたち!今日は、TypeScriptのより奥深い型の一つ、never型について一緒に掘り下げてみましょう。プログラミングが新しいあなたも心配しないでください。私はこの概念をステップバイステップでガイドします。これまでに数多くの学生を指導してきました。お気に入りの飲み物を片手に、TypeScriptの世界への興奮的な旅に出発しましょう!

TypeScript - Never

never型とは何か?

TypeScriptにおけるnever型は、しばしば「ボトムタイプ」や「空の型」と呼ばれます。これは決して発生すべきではない型を表します。あなたは思うかもしれません、「決して発生しない型が必要なのはなぜか?」。好奇心旺盛なあなたたちに、これはもっと有用です!

never型はいつ使われる?

  1. 不可能なシナリオを表すため
  2. 完全なチェックを処理するため
  3. 返さない関数で

これらの概念を明確にするために、いくつかの例を見てみましょう。

例1: 不可能なシナリオの表現

function throwError(message: string): never {
throw new Error(message);
}

let result = throwError("Oops! Something went wrong!");
console.log(result); // この行は決して達成されません

この例では、throwError関数はエラーを投げて正常に返さないことを保証しています。したがって、その返り値の型はneverです。

これを考えると、もしケーキのレシピに「決して焼かないでください」と書かれていたら、そのケーキはオーブンから出てこないことを知っていますよね!

例2: 完全なチェック

type Shape = "circle" | "square" | "triangle";

function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return Math.PI * Math.pow(5, 2);
case "square":
return 10 * 10;
case "triangle":
return (10 * 5) / 2;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}

ここでneverは、私たちがすべての可能性をカバーしていることを確認するのに役立ちます。Shape型に新しい形を追加しても、getAreaにその場合のケースを追加忘れた場合、TypeScriptはエラーを返します。忘れたことを思い出させてくれる便利なアシスタントのようなものです!

例3: 返さない関数

function infiniteLoop(): never {
while (true) {
console.log("This loop never ends!");
}
}

この関数は永久に(またはコンピュータのメモリが尽きるまで)実行されます。since it never finishes executing, its return type is never. これは友達に「決して話をやめる」と言うようなものです。彼らは長い会話が待っていることを知っています!

never型とvoid型の違い

今、あなたは思うかもしれません、「never」と「void」の違いは何か?」。素晴らしい質問です!それを分解してみましょう。

void

void型は、関数が何かの値を返さない場合に使用されますが、実行は完了します。

function logMessage(message: string): void {
console.log(message);
}

logMessage("Hello, TypeScript!"); // この関数はundefinedを返します

never

一方、never型は関数が実行を完了しない、または常にエラーを投げる場合に使用されます。

function failwithError(message: string): never {
throw new Error(message);
}

failwithError("This function never returns!");

これを考えると、voidは店に行って何も持って帰ること、neverは目的地のない旅に出て、決して帰らないことです!

neverの実用的な用途

neverのさらに実用的な例を見てみましょう。

例4: タイプガード

type Square = { kind: "square", size: number };
type Circle = { kind: "circle", radius: number };
type Shape = Square | Circle;

function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}

function getArea(shape: Shape) {
switch (shape.kind) {
case "square": return shape.size * shape.size;
case "circle": return Math.PI * shape.radius ** 2;
default: return assertNever(shape); // ShapeがSquareでもCircleでもない場合にエラー
}
}

この例では、assertNeverは私たちが見落とす可能性のあるケースをキャッチするのに役立ちます。タイプのジャグリングを学ぶときのセーフティネットのようなものです!

例5: 到達しないコードの検出

function neverReaches(): never {
while (true) {
// Some operation
}
console.log("This line will never be reached");  // TypeScriptエラー
}

TypeScriptは、console.log文が決して達成されないことを知っており、エラーを返します。不存在の目的地に車を運転しようとしているときにGPSが警告を発するようなものです!

neverのメソッドとプロパティ

今、あなたは思うかもしれません、「never」にはメソッドやプロパティがあるのか?」。真実は、neverは決して発生すべきではない型を表すため、自身のメソッドやプロパティはありません。しかし、それはTypeScriptの型システムの重要な一部です。

以下に、neverでできる(またはできない)ことをまとめた表を示します:

操作 結果 説明
neverに代入 ✅ 許可 どの型もneverに代入できます
neverを他の型に代入 ❌ 許可されません neverは他の型に代入できません
neverメソッドの呼び出し ❌ 許可されません neverは決して発生しないため、メソッドを呼び出すことはできません
neverのユニオン使用 ✅ 許可されていますが効果はありません neverはユニオン型で無視されます
neverの交差使用 ✅ 許可され、結果はneverです どの型もneverと交差するとneverになります

結論

そして、私の親愛なる学生们、私たちは「never」の土地を旅しました。その隅々まで探求しました。覚えておいてください、「never」は、約束の友達のように、決して現れないかもしれませんが、それでも重要です!

「never」を理解するのは最初は難しいように思えるかもしれませんが、練習を積むと、TypeScriptのツールボックスにおける貴重なツールになるでしょう。コードをより頑丈にし、潜在的なエラーをキャッチし、関数の行動を深く考えるのに役立ちます。

codingを続け、学び続け、TypeScriptで新しいことを試すことに「never」を言わないでください!次回までに、コンパイルエラーが少なく、型の推論が強いことを祈っています!

Credits: Image by storyset