SODA Engineering Blog
🦁

TypeScript意外と忘れがちな機能

2024/08/29に公開

こんにちは。FEチームのMapleです。私たちのチームは、現在のシステムアーキテクチャを見直し、Reactを用いた新しいアーキテクチャへの移行を検討しています。今回は先日TypeScriptの型について解説を行ったので、意外と忘れがちな機能について解説出来たらなと思います。

はじめに

  • 私が思った意外と忘れがちな機能なので、人によるかもしれません
  • 今回はコードを長めに実際に導入されそうな感じで記述出来たらなと思います

Conditional Typesの応用

TypeScriptの条件付き型(Conditional Types)は、型を柔軟に操作する強力なツールです。T extends U ? X : Yの形式で、条件に応じた型を返すことができます。

Conditional Typesの例

この例では、FetchStatus型が、FetchResponseにerrorプロパティが存在するかどうかによって、"failed"か"success"を返します。

type FetchStatus<T> = T extends { error: Error } ? "failed" : "success";

interface FetchResponse {
  data: string;
  error?: Error;
}

type Status = FetchStatus<FetchResponse>;  // "success" または "failed"

Template Literal Typesの威力

テンプレートリテラル型を使うことで、文字列操作を型レベルで行えます。

Template Literal Typesの例

この例では、welcome_enやwelcome_jaのように、指定されたロケールIDに基づいたメッセージIDを扱うことができます。

type LocaleIDs = "en" | "ja" | "fr";
type LocaleMessageIDs = `welcome_${LocaleIDs}`;

const welcomeMessage: LocaleMessageIDs = "welcome_en";  // 正しい
const invalidMessage: LocaleMessageIDs = "welcome_de";  // エラー: "welcome_de"は型に適合しません

Mapped Typesで動的に型を作成

Mapped Typesを使うと、既存の型を元に新しい型を作成できます。

Mapped Typesの例

この例では、すべてのプロパティがオプショナルなOptionalTodo型を作成しました。

全部に?を付けているコードよく見ますよね

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type OptionalTodo = Partial<Todo>;  // すべてのプロパティがオプショナルになる
const todo: OptionalTodo = {
  title: "Learn TypeScript"
}

Lookup Typesを使った型の絞り込み

Lookup Typesを使うと、オブジェクト型の特定のプロパティの型を取得できます。
複雑な型の一部を再利用したり、絞り込んだりすることができます。

Lookup Typesを使った型の絞り込みの例

この例では、User型のpreferencesプロパティのthemeの型を抽出し、UserTheme型として利用しています。

interface User {
  id: number;
  name: string;
  preferences: {
    theme: "light" | "dark";
    notifications: boolean;
  };
}

type UserTheme = User['preferences']['theme'];  // "light" | "dark

UnionsとIntersectionsの使い方

Union型とIntersection型を効果的に組み合わせることで、より良い型システムを構築できます。

UnionsとIntersectionsの例

この例では、ApiResponse型がErrorResponseとDataResponseのいずれかで、レスポンスがどちらの型かによって処理を分岐させています。

interface ErrorResponse {
  error: string;
}

interface DataResponse {
  data: string;
}

type ApiResponse = ErrorResponse | DataResponse;

function handleResponse(response: ApiResponse) {
  if ('error' in response) {
    console.error(`Error: ${response.error}`);
  } else {
    console.log(`Data: ${response.data}`);
  }

まとめ

  • 意外と忘れがちな機能がありますが、キャッチアップしつつプロダクトに導入出来たらなとおもいます。
  • 今度はもっとより詳しく型を解説出来たらなと思います。
SODA Engineering Blog
SODA Engineering Blog

Discussion