型ガードで TypeScript コードを強化!わかりやすい解説と実践例

2023.08.05 に更新記事は 9 分で読めます
サムネイル画像

この記事では TypeScript の型ガードについて、分かりやすく具体的なコード例とともに解説していきます。

型ガードって何? 🤔

まず、型ガードとは TypeScript の便利な機能の1つで 特定の型に属する変数を安全 に扱う方法です。

型ガードは特定の型が期待される場所で実際にその型が利用されているかどうかを判断することができます。

実際のコードを見ていきましょう!

function isString(value: any): value is string {
  return typeof value === "string";
}

let value: any = "Hello, TypeScript!";
if (isString(value)) {
  console.log(value.toUpperCase()); // HELLO, TYPESCRIPT!
}

上記のコードでは isString 関数が型ガードの役割を果たしています。

valuestring 型であることを確認した後、安全に toUpperCase メソッドを実行しています。

なぜ型ガードが必要なの? 🧐

「TypeScript の型ガードがどんなものかわかったけど、どうして必要になるんだろう?」
次はそんな疑問に対して、なぜ型ガードが必要なのかを解説していきます。

型ガードを使うことで 型エラーを事前に防ぐことができる ため、コードが安全かつ堅牢になります。
そのため、TypeScript の 強力な型システムを最大限活用 するために型ガードが重要になってきます。

function printLength(value: string | number) {
  if (typeof value === "string") {
    console.log(value.length); // 文字列の場合、length を出力
  } else {
    console.log(value); // 数値の場合、そのまま出力
  }
}

printLength("Hello, TypeScript!"); // 18
printLength(42); // 42

上記の例では printLength 関数は string 型と number 型のどちらも受け取れますが、型ガードを使って渡された型に対しての適切な処理を行っています。

組み込みの型ガード 🏗

TypeScript にはいくつかの組み込み型ガードがあり、その中でも最も基本的なものが 組み込みの型ガード と呼ばれるものです!

組み込みの型ガードの代表的なものには typeofinstanceof があります。

typeof

typeof は、変数のデータ型を判断するための機能です。

主にプリミティブ型と呼ばれる基本的な型(stringnumberbooleansymbolbigintundefinedobject)を判定する際に使用されます。

以下の例を見てみましょう!

function getType(value: string | number) {
  if (typeof value === "string") {
    console.log("This is a string!");
  } else {
    console.log("This is a number!");
  }
}

getType("Hello, TypeScript!"); // This is a string!
getType(42); // This is a number!

上記のコードでは getType 関数は string 型と number 型のどちらも受け取れますが、typeof を使って引数に応じて適切な処理を行っています。

instanceof

instanceof は、オブジェクトが特定のクラスのインスタンスであるかどうかを判断する機能です。

基本的にはクラスやインターフェースを使ったカスタム型の判定に使用されます。

実際のコードを見てみましょう。

class Person {
  constructor(public name: string) {}
}

class Animal {
  constructor(public species: string) {}
}

function printInfo(value: Person | Animal) {
  if (value instanceof Person) {
    console.log(`こんにちは、 ${value.name}!`);
  } else {
    console.log(`これは ${value.species} です`);
  }
}

printInfo(new Person("ジョン")); // こんにちは、ジョン!
printInfo(new Animal("犬")); // これは 犬 です

printInfo 関数は Person 型と Animal 型のどちらも受け取れますが、引数である instanceof を使って適切な処理を行っています。

このように、組み込み型ガードを使うと型安全なコードを簡単に実現できます。

これらの機能は TypeScript の型システムを活用して、より優れたコードを書くために大事なのでぜひ覚えておきましょう!

自分で定義した型に型ガードを適用しよう 🛠

illust

型ガードを使っていると、自分で定義した型に対して型ガード を使いたくなる場面が多々あります。
そんなときに使えるカスタム型に型ガードを適用する方法を見ていきましょう。

自分が定義した型に対する型ガードは value is Type の形式を持つ関数を定義することで実現できます。

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

function isCircle(shape: Shape): shape is Circle {
  return shape.kind === "circle";
}

function getArea(shape: Shape) {
  if (isCircle(shape)) {
    return Math.PI * shape.radius ** 2;
  } else {
    return shape.sideLength ** 2;
  }
}

const circle: Circle = { kind: "circle", radius: 5 };
const square: Square = { kind: "square", sideLength: 4 };

console.log(getArea(circle)); // 78.53981633974483
console.log(getArea(square)); // 16

上記のコードでは Shape 型を Circle 型と Square 型のどちらも入る型として定義しています。

これに対して getArea 関数を実行し、その中で引数として渡されたのが Circle 型と Square どちらかを判断して処理の内容を変更しています。

このときに isCircle 関数を使って、shape.kind === "circle" の条件に一致するものは Circle 型である、とすることで型ガードを実現しています。

実践的な型ガードの例 🌟

最後に、より実践的な型ガードの例として API のレスポンスを安全に処理する方法を紹介します!

interface ApiResponse {
  status: number;
  data?: any;
  error?: string;
}

function isSuccess(response: ApiResponse): response is ApiResponse & { data: any } {
  return response.status >= 200 && response.status < 300;
}

function handleError(response: ApiResponse) {
  if (isSuccess(response)) {
    console.log("Success!", response.data);
  } else {
    console.error("Error:", response.error);
  }
}

const successResponse: ApiResponse = { status: 200, data: { message: "Success!" } };
const errorResponse: ApiResponse = { status: 500, error: "Internal Server Error" };

handleError(successResponse); // Success! { message: 'Success!' }
handleError(errorResponse); // Error: Internal Server Error

まず、ApiResponse インターフェースを定義して、API レスポンスの形状を表現しています。
status プロパティは HTTP ステータスコードを表していて、成功時には data プロパティが、失敗時には error プロパティが存在します。

次に isSuccess 関数を定義しています。
この関数は引数として ApiResponse 型の値を受け受け取って、そのレスポンスが成功かどうかを判断する型ガードです。
HTTP ステータスコードが 200 以上 300 未満の場合に成功と判断して、data プロパティの存在が保証されています。

最後に handleError 関数を定義しています。
この関数は isSuccess 関数を使って引数として渡された ApiResponse 型の値が成功かどうかを判断してそれに応じて適切な処理を行います。
成功時には Success!data を出力し、失敗時には Error: と error を出力します。

この実践的な例では、型ガードを使うことで API レスポンスを安全に処理してコードの品質を向上させています!

このように型ガードを使うことで、より型安全なコードを書くことができるためバグの発生を抑えることができます。
ぜひ実践的なコードに型ガードを活用してみてください!

プロフィールアイコン

Syuu

フロントエンドが好きなWEBエンジニア Next.js / React / TypeScript

SHARE