TypeScript の特殊な型: never、void、unknown、any

TypeScript には4つの特殊な型があります: never、void、unknown、any。

これらは型システムにおいて重要な役割を果たし、それらの違いを理解することは型安全なコードを書くために不可欠です。


SVG 図: 特殊な型の比較 背景 タイトル 4つの特殊な型の比較 any 任意の型 型チェックを迂回 安全性なし unknown 安全な任意の型 使用にはチェックが必要 安全性あり void 戻り値なし 関数に使用 undefined を返せる never 決して返らない 例外を投げる/ループ すべての型のサブタイプ 矢印マーク 下半分: 型安全レベル 型安全レベル レベル1 any(最低) 型チェックを完全に迂回 レベル2 unknown 型チェック後に使用が必要 レベル3 void 関数の戻り値なし レベル4 never(最高) 最も厳格で、最も安全

なぜ特殊な型が必要か

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 を使用するときは、先に型チェックを行う必要があります。

例

// 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 で最も安全でない型です。

例

// any は任意の型になりえます
// 型チェックを完全に迂回します
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 を使用します。

その他の拡張