TypeScript の特殊な型
TypeScriptには、型システムにおいて特定の振る舞いを持ついくつかの特殊な型(Special Types)が含まれています。
これらの型は、型が事前に判明していないケースや、JavaScriptのプリミティブを型安全(Type-safe)な方法で扱う必要があるさまざまなシナリオで使用されます。
注意: これらの特殊な型はTypeScriptの型システムの一部であり、コードの型安全性を高め、自己文書化(Self-documenting)を助ける役割を果たします。
1. any 型
any 型は、TypeScriptにおいて最も柔軟な型です。
本質的には、特定の変数に対してタイプチェック(Type checking)をスキップするようコンパイラに指示します。
特定の状況では便利ですが、TypeScriptの型安全機能の多くをバイパスしてしまうため、使用は最小限にとどめるべきです。
1.1 any を使用する場面
- JavaScriptのコードをTypeScriptに移行する場合
- 型が不明な動的コンテンツ(Dynamic content)を扱う場合
- 特定のケースで意図的に型チェックを無効化したい場合
以下の例は any を使用していないため、エラーが発生します。
例:any なし(エラー発生)
let u = true;
u = "string"; // エラー: 型 'string' を型 'boolean' に割り当てることはできません。
Math.round(u); // エラー: 型 'boolean' の引数を型 'number' のパラメータに割り当てることはできません。変数を特殊な型 any に設定すると、タイプチェックが無効になります。
例:any あり
let v: any = true;
v = "string"; // any型なのでエラーになりません
Math.round(v); // any型なのでエラーになりませんany はタイプチェックを無効にするためエラーを回避する便利な方法に見えますが、TypeScriptによる型安全性は提供されず、オートコンプリート(入力補完)などの型データに依存するツールの機能も動作しなくなります。
「いかなる(any)」コストを払っても、any の多用は避けるべきであることを覚えておいてください。
2. unknown 型
unknown 型は、any に対する型安全なカウンターパート(対応する型)です。
「これは何にでもなり得るが、使用する前に何らかの型チェックを行う必要がある」ということを型安全に伝えるための方法です。
2.1 unknown と any の主な違い
unknownは使用前に型チェック(Type-check)が必要。- 型アサーション(Type assertion)なしでは
unknown型のプロパティにアクセスできない。 unknown型の値を呼び出したり、コンストラクトしたりすることはできない。
TypeScriptは、以下の例のように適切な型チェックなしで unknown 型が使用されるのを防ぎます。
例:unknown の利用
let w: unknown = 1;
w = "string"; // エラーなし
w = {
runANonExistentMethod: () => {
console.log("我思う、ゆえに我あり");
}
} as { runANonExistentMethod: () => void };
// 型が不明なとき、以下のコメントアウトされたコードのエラーをどう回避すべきか?
// w.runANonExistentMethod(); // エラー: オブジェクトの型は 'unknown' です。
if (typeof w === 'object' && w !== null) {
// if文でのチェックにより、安全にキャスト(Casting)して実行できる
(w as { runANonExistentMethod: Function }).runANonExistentMethod();
}2.2 unknown を使用する場面
- 外部ソース(API、ユーザー入力など)からのデータを扱う場合
- 柔軟性を維持しつつ、型安全性を確保したい場合
- JavaScriptからTypeScriptへ型安全な方法で移行する場合
2.3 unknown による型絞り込み(Type narrowing)
型ガード(Type guards)を使用して、unknown 値の型を絞り込むことができます。
function processValue(value: unknown) {
if (typeof value === 'string') {
// ここで value は string として扱われる
console.log(value.toUpperCase());
} else if (Array.isArray(value)) {
// ここで value は any[] として扱われる
console.log(value.length);
}
}3. never 型
never 型は、決して発生することのない値の型を表します。
何かが決して起こらない、あるいは起こるべきではないことを示すために使用されます。
3.1 never の一般的なユースケース
- 決して戻らない関数(常にエラーをスローするか、無限ループに入る関数)
- タイプチェックを絶対にパスしない型ガード
- ディスクリミネイテッド・ユニオン(Discriminated unions)における網羅性チェック(Exhaustiveness checking)
3.2 never 型の活用例
1. 値を返さない関数
function throwError(message: string): never {
throw new Error(message);
}2. ディスクリミネイテッド・ユニオンによる網羅性チェック
type Shape = Circle | Square | Triangle;
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength ** 2;
default:
// TypeScriptはここが実行されないことを認識している
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}3. 基本的な never 型(代入時にエラーが発生)
let x: never = true; // エラー: 型 'boolean' を型 'never' に割り当てることはできません。4. undefined と null
TypeScriptでは、undefined と null は string や number と同様に、それぞれ独自の型を持っています。
デフォルトでは、これらの型は他の任意の型に代入可能ですが、TypeScriptの厳密なNullチェック(Strict null checks)設定によって挙動が変わります。
4.1 undefined と null のポイント
undefinedは、変数が宣言されたが値が代入されていないことを意味します。nullは、値が存在しない、あるいはオブジェクトが存在しないことを表す明示的な代入です。strictNullChecksが有効な場合、これらの型を明示的に処理する必要があります。
基本的な使用法
let y: undefined = undefined;
let z: null = null;4.2 オプションパラメータとプロパティ
// オプションパラメータ(暗黙的に `string | undefined` となる)
function greet(name?: string) {
return `こんにちは、${name || 'ゲスト'}さん`;
}
// インターフェース内のオプションプロパティ
interface User {
name: string;
age?: number; // `number | undefined` と同等
}4.3 Null合体演算子とオプショナルチェイニング
// Null合体演算子 (??) - 値が null または undefined の場合のみデフォルト値を使用
const value = input ?? 'デフォルト値';
// オプショナルチェイニング (?.) - 入れ子になったプロパティに安全にアクセス
const street = user?.address?.street; 重要: これらの型は、tsconfig.json ファイルで strictNullChecks が有効になっている場合に最も威力を発揮します。これにより、null や undefined がそれ自身および any 型にのみ代入可能であることが保証されます。
厳密なNullチェックを有効にするには、tsconfig.json に以下を追加します。
{
"compilerOptions": {
"strictNullChecks": true
}
}