HapInSアドベントカレンダー2024、6日目! 毎朝最大5kmジョギングしていつも疲れ果てているh_shimakawaです。
今回はTypeScriptの型について記事を書きました!
はじめに
TypeScriptで、成功・失敗などの状態にで型が変わる値を扱う際、 optionalなプロパティまみれになった経験はありませんか?
特に、フロントエンド開発において非同期処理の結果を管理する場合、こうした状況に直面することが多いです。 私はreactのhooksを使った開発では、WebApiがloading|success|errorの値を返すことが多く、よく悩んでいました。
この記事では、そのような場合に便利な「タグ付きunion型」を使った型定義の方法を紹介します。
目次
問題が発生する型定義
まずは以下のgetUserInfo()関数を見てください。 成功時と失敗時で異なる型の値を返しています。
enum Status { Success = "success", Error = "error", } type User = { id: number; name: string; } const getUserInfo = (id: number) => { if (id === 1) { // 取得成功。 return { status: Status.Success, user: { id: 1, name: "Taro" }, // User型 }; } else { // エラー return { status: Status.Error, errorLog: "User not found", }; } }
上記コードの問題
この関数を使うと、以下のような問題が発生します。
const result = getUserInfo(x); // 備考: getUserInfoの型は以下のようになっている // type Response = { // status: string; // user?: User; // errorLog?: string; // }; if(result.status === Status.Success){ // userの型がoptionalなので、コンパイルエラーを防ぐには冗長なチェックが必要。 doSomeThing(result.user.name); // Error: result.user is possibly 'undefined'. }else{ // エラー時にもuserプロパティが存在してしまうため、不適切にアクセス可能。 console.log("error:", result.errorLog, result.user?.id); }
問題点の整理
- statusによる判別が可能ですが、型が明確に区別されていません。
- プロパティがすべてoptionalになり、冗長なチェックが必要。
- エラー時にresult.userが無効であることを型で保証できないため、型安全性ではありません。
タグ付きユニオン型とは?
タグ付きユニオン型(Tagged Union Type)は、異なる型のオブジェクトを、1つの型で型安全に扱うための方法です。 識別用のタグ(statusなど)を使うことで、各ケースの処理を型安全に記述できます。
以下に例を示します。
type SuccessResult = { status: Status.Success; // 成功時のタグ。Status型の中でも特定の値を示す user: User; }; type ErrorResult = { status: Status.Error; // 失敗時のタグ。こちらも具体的な値 errorLog: string; }; // タグ付きユニオン型の定義 type Result = SuccessResult | ErrorResult;
この型定義により、成功時とエラー時のレスポンスを明確に区別し、それぞれの型にあるプロパティだけをアクセスできます。
const getUserInfo = (id: number): Result => { if (id === 1) { return { status: Status.Success, user: { id: 1, name: "Taro" }, }; } else { return { status: Status.Error, errorLog: "User not found", }; } } const result = getUserInfo(1); switch (result.status) { case Status.Success: // resultはSuccessResult型と推論され、result.userは確実に存在する console.log(result.user.name); // 型安全にアクセスできる break; case Status.Error: console.error(result.errorLog); // 存在しないプロパティへのアクセスは型エラーで防止される // console.error(result.user?.name); break; }
タグ付きユニオン型の利点
- 成功時とエラー時の型が明確に区別でき、型安全性が向上。
- 冗長なoptionalチェックが不要。
- エディタの補完機能が正確になるので、快適なコーディングができます。
まとめ
タグ付きユニオン型を使うことで、状態ごとに型を明確に区別でき、型安全にプログラミングができます。 特に非同期処理を扱う時に有用です。 皆さんも使ってみませんか?