【TypeScript】unknown型とnever型 - 実践的な使用法

【TypeScript】unknown型とnever型 - 実践的な使用法

2024-10-25

2024-10-25

TypeScriptunknown型とnever型は、他の言語にはあまり見られないユニークな型であり、型安全性を向上させ、予期しない動作を防ぐために非常に役立ちます。本記事では、unknown型とnever型の違いや、それぞれの実践的な使用方法について解説し、より堅牢なTypeScriptコードを実現するための手法を紹介します。

unknown型とは?

unknown型は、「どんな値でも受け取れるが、その値を使用する前に必ず型チェックをしなければならない型」です。unknownは、any型に似ていますが、値を扱う際に安全な型チェックが必要である点で異なります。

unknown型の基本的な使い方

例えば、APIレスポンスやユーザーからの入力のように、型が事前にわからないデータを処理する場合にunknown型を使用します。

let input: unknown;
input = "Hello";  // OK
input = 42;       // OK
input = true;     // OK

このように、unknown型はどんな型の値でも代入可能です。しかし、unknown型の値をそのまま使用しようとするとエラーが発生します。値を安全に操作するためには、事前に型チェックを行う必要があります。

let input: unknown;
input = "Hello";
console.log(input.length); // エラー: 'input'は'unknown'型なのでプロパティにアクセスできません

上記のコードでは、inputunknown型のため、直接lengthプロパティにアクセスすることはできません。そこで、型チェックを行ってから値を操作する必要があります。

型チェックとunknown型の安全な使用

unknown型の値を使用する際には、typeofinstanceofを使って型チェックを行います。これにより、予期しない型によるエラーを防ぎ、コードの安全性が向上します。

function printLength(input: unknown) {
  if (typeof input === "string") {
    console.log(input.length);  // 型チェック後なのでOK
  } else {
    console.log("Not a string");
  }
}
printLength("Hello");  // 5
printLength(42);       // Not a string

このように、unknown型は明確な型チェックを行うことで安全に使用でき、any型と比較してより堅牢な型安全性を提供します。

APIレスポンスの例

unknown型は、APIからの不確定なレスポンスを処理する際にも役立ちます。たとえば、外部APIのレスポンスデータを事前に予測できない場合、unknown型を使い、取得後に型を判定して安全に処理を行います。

async function fetchData(): Promise<unknown> {
  // APIからデータを取得
  return await fetch("/api/data").then((res) => res.json());
}
async function processData() {
  const data = await fetchData();
  if (typeof data === "object" && data !== null && "userId" in data) {
    console.log((data as { userId: number }).userId);  // 型チェック後に型キャスト
  } else {
    console.log("Invalid data format");
  }
}
processData();

この例では、APIレスポンスの型が不明であるため、unknown型を使用し、レスポンスの内容に応じて安全に型キャストしています。

never型とは?

never型は、「決して値を返さないことを示す型」であり、主に次のような場面で使用されます。

  1. 関数が例外を投げる
  2. 無限ループで終了しない
  3. 型推論で「絶対に発生しない」型を表す never型は、値が返らないことを明示的に示すことで、開発者に対して関数の振る舞いを正確に伝える役割を果たします。

never型の基本的な使い方

例えば、エラーを発生させてプログラムを強制終了させる関数には、never型が使用されます。

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

このthrowError関数は常にエラーを投げるため、決して正常な値を返しません。そのため、戻り値の型としてneverが適切です。

無限ループとnever型

また、無限ループを持つ関数も決して戻り値を返さないため、never型を指定することができます。

function infiniteLoop(): never {
  while (true) {
    // 永遠に実行される
  }
}

このように、never型は関数が「正常に終了しない」ことを明示するため、コードの意図が分かりやすくなります。

型推論でのnever型

TypeScriptは型推論によって、switch文などで発生し得ないケースに対してもnever型を自動的に割り当てることがあります。これにより、意図しない型の処理ミスを防ぐことができます。

type Shape = "circle" | "square";
function getArea(shape: Shape) {
  switch (shape) {
    case "circle":
      return Math.PI * 1 * 1;
    case "square":
      return 1 * 1;
    default:
      const _exhaustiveCheck: never = shape;
      throw new Error(`Unknown shape: ${_exhaustiveCheck}`);
  }
}

この例では、Shape型に存在しない型がswitch文に追加された場合、自動的にnever型として推論され、エラーが発生するため、未処理のケースを防げます。

unknown型とnever型の違い

unknown型とnever型は、どちらもTypeScriptの型システムにおいて特定の役割を果たしますが、その目的と使用方法には明確な違いがあります。

  • unknown型
    不明な型で、型チェックが必要な型。任意の値を受け入れますが、使用前に型の確認が必要。
  • never型
    決して値を返さない型。関数が終了しないことや例外を投げることを明示します。 これらの型を適切に使い分けることで、コードの安全性と可読性が向上し、予期しないエラーを未然に防ぐことができます。

まとめ

TypeScriptにおけるunknown型とnever型は、それぞれ特定の場面で非常に有効な型です。 unknown型は、不明な型を安全に扱い、明確な型チェックを必要とする場合に使用され、never型は、値を決して返さない関数や、発生し得ない型のチェックに役立ちます。 これらの型を正しく活用することで、コードの型安全性を高め、予期しないエラーを防止することができます。TypeScriptの型システムを活用して、より堅牢で保守性の高いコードを書いていきましょう。

Recommend