TypeScript の特殊な型: never、void、unknown、any
TypeScript には4つの特殊な型があります: never、void、unknown、any。
これらは型システムにおいて重要な役割を果たし、それらの違いを理解することは型安全なコードを書くために不可欠です。
SVG 図: 特殊な型の比較
なぜ特殊な型が必要か
TypeScript の型システムは非常に強力で、通常の型に加えて、特定のシナリオを処理するための4つの特殊な型を提供しています。
これら4つの特殊な型の違いを理解することで、より安全で正確な型コードを書くことができます。
概念の説明:never、void、unknown、any は TypeScript の特殊な型であり、それぞれ異なる意味と使用シーンを持ちます。
never 型
never は決して戻らない型を表します。通常、例外を投げる関数や無限ループする関数に使われます。
例
// 関数は正常に戻ることはなく、常にエラーを投げます
function throwError(message: string): never {
throw new Error(message);
}
// 無限ループする関数
// 関数は決して戻らず、プログラムは動き続けます
function infiniteLoop(): never {
while (true) {
console.log("実行中...");
}
}
// never はすべての型のサブタイプです
// つまり never は任意の型に代入できます
var neverValue: never;
var num: number = neverValue; // 正しい: never は number のサブタイプです
console.log("never を number に代入: " + num);
実行結果:
never 赋值给 number: undefined
重要:never はすべての型のサブタイプです。つまり、never は任意の型に代入できますが、任意の型は never に代入できません(never 自身を除く)。
void 型
void は戻り値がないことを表し、通常 return 文のない関数の宣言に使われます。
例
// 関数実行後に戻り値がありません
function logMessage(message: string): void {
console.log("ログ: " + message);
// return 文がないか、undefined を返します
}
logMessage("Hello");
// void 変数(ほとんど使われません)
// undefined のみ代入できます
var empty: void = undefined;
console.log("void 変数: " + empty);
実行結果:
日志: Hello void 变量: undefined
説明:void は実際には undefined ととても似ており、主に関数の戻り値型の宣言に使われ、この関数に戻り値がないことを表します。
unknown 型
unknown は型安全な any です。unknown を使用するときは、先に型チェックを行う必要があります。
例
// これは「安全な」 any です
var value: unknown = "hello";
value = 42;
value = true;
// 未知の型は他の型に直接代入できません
// 次の行のコメントを外すと、コンパイラがエラーを報告します
// var str: string = value;
// 型チェックを行った後にのみ代入できます
if (typeof value === "string") {
// TypeScript は value が string 型であることを知っています
var str: string = value;
console.log("文字列の長さ: " + str.length);
}
実行結果:
字符串长度: 5
安全性:unknown は使用前に型チェックを強制するため、型安全性を保護します。未知のソースからのデータを扱う場合は、any ではなく unknown を使うべきです。
any 型
any は型チェックを完全に迂回し、任意の型に代入できます。これは TypeScript で最も安全でない型です。
例
// 型チェックを完全に迂回します
var anything: any = "hello";
anything = 42;
anything = true;
// any は任意の型に代入できます
// これは型安全性を壊します
var str: string = anything;
var num: number = anything;
console.log("文字列: " + str);
console.log("数値: " + num);
// any 型のオブジェクトは任意のメソッドを呼び出せます
// コンパイラはエラーを報告しませんが、実行時にエラーになる可能性があります
var obj: any = {};
obj.foo(); // エラーにはなりませんが、実際には foo メソッドは存在しません
obj.bar = "value";
警告:any 型は TypeScript の型チェックを完全に迂回するため、使用はできるだけ避けるべきです。やむを得ず使用する場合は、値の型が正しいことを確認してください。
any vs unknown の比較
any と unknown はどちらも任意の型を受け入れますが、安全性はまったく異なります。
| 特性 | any | unknown |
|---|---|---|
| 任意の型を受け入れる | ✓ はい | ✓ はい |
| 他の型に直接代入できる | ✓ はい | ✗ いいえ(チェックが必要) |
| メソッドを直接呼び出せる | ✓ はい | ✗ いいえ(チェックが必要) |
| 型安全性 | ✗ なし | ✓ あり |
推奨:未知の型のデータを扱う場合は、any ではなく unknown を優先的に使うことで、型チェックを強制できます。
never vs void
never と void はどちらも関数の戻り値型に使われますが、意味はまったく異なります。
| 特性 | never | void |
|---|---|---|
| 意味 | 決して返らない | 戻り値がない |
| 関数に使う | 例外を投げる/無限ループ | 普通の戻り値のない関数 |
| 他の型に代入できる | ✓ はい(サブタイプ) | void/any にのみ代入できる |
重要な違い:never は関数が正常に戻ることは決してない(例外を投げるか無限ループする)ことを表します。void は関数が正常に実行完了するが、戻り値がないことを表します。
実践応用: Exhaustive Check
never 型の重要な用途の1つは、すべての可能なケースが処理されることを保証するための網羅チェック(exhaustive check)を行うことです。
例
type Shape = { kind: "circle", radius: number }
| { kind: "square", side: number };
// 図形の面積を計算
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.side ** 2;
default:
// default 分岐は予期しないすべてのケースを処理する必要があります
// 新しい Shape 型を追加しても処理を忘れた場合、コンパイラがエラーを報告します
var _exhaustive: never = shape;
return _exhaustive;
}
}
var circle = { kind: "circle" as const, radius: 5 };
var square = { kind: "square" as const, side: 4 };
console.log("円の面積: " + area(circle).toFixed(2));
console.log("正方形の面積: " + area(square));
実行結果:
圆形面积: 78.54 正方形面积: 16
Exhaustive Check:このパターンは、新しい型の分岐を追加したときに switch での処理を忘れると、追加された型は never に代入できないため、コンパイラがエラーを報告することを保証します。
注意事項
- never はサブタイプ:never は任意の型に代入できますが、任意の型は never に代入できません
- void vs undefined:JavaScript では、void と undefined は基本的に同等です
- unknown はチェックが必要:unknown 型の値を使用する前に、型チェックを行う必要があります
- any を避ける:できるだけ any を使用せず、unknown を優先的に使用してください
ベストプラクティス:型安全性の高い順:never > void > unknown > any。より安全な型を使用してください。
まとめ
これら4つの特殊な型を理解することは、高品質な TypeScript コードを書くために非常に重要です。
- never:決して戻らない。exhaustive check に使用され、すべての型のサブタイプです
- void:戻り値なし。通常の戻り値のない関数に使用されます
- unknown:安全な任意の型。使用前に型チェックを行う必要があります
- any:すべての型チェックを回避する。使用を避けるべきです
その他の拡張提案:コードを書くときは、より厳格な型を優先的に使用してください。未知のデータを扱う場合は unknown、関数に戻り値がない場合は void、網羅的なチェックが必要な場合は never を使用します。