【初心者向け】TypeScript: ユニオン型とリテラル型プロパティの連携でスマートな型推論を実現

2024-06-20

TypeScriptにおけるリテラル型プロパティを用いたユニオン型からの型選択

例題

まず、以下の例題を見てみましょう。

type UserRole = "admin" | "editor" | "guest";

type User = {
  id: number;
  name: string;
  role: UserRole;
};

function isAdmin(user: User): user is { role: "admin" } {
  return user.role === "admin";
}

const user1: User = {
  id: 1,
  name: "Taro",
  role: "admin"
};

const user2: User = {
  id: 2,
  name: "Jiro",
  role: "editor"
};

if (isAdmin(user1)) {
  console.log(user1.id); // 型 'number' として推論される
} else {
  console.log(user1.name); // 型 'string' として推論される
}

この例題では、User型はidnameroleプロパティを持つオブジェクトを表します。roleプロパティの型は、admineditorguestのいずれかのリテラル型です。

isAdmin関数は、引数として渡されたUser型のオブジェクトがroleプロパティに"admin"を持つかどうかを判定します。判定結果に基づいて、userオブジェクトの型を具体的に絞り込むことができます。

解説

上記の例題で、isAdmin関数内で行われている型絞り込みについて詳しく解説します。

このように、TypeScriptのリテラル型プロパティを用いることで、ユニオン型から具体的な型を効率的に選択し、型安全なコードを書くことができます。この機能を活用することで、コードの可読性と保守性を向上させることができます。




type UserRole = "admin" | "editor" | "guest";

type User = {
  id: number;
  name: string;
  role: UserRole;
};

function isAdmin(user: User): user is { role: "admin" } {
  return user.role === "admin";
}

const user1: User = {
  id: 1,
  name: "Taro",
  role: "admin"
};

const user2: User = {
  id: 2,
  name: "Jiro",
  role: "editor"
};

if (isAdmin(user1)) {
  console.log(user1.id); // 型 'number' として推論される
  console.log(user1.name); // 型 'string' として推論される
} else {
  console.log(user1.id); // 型 'number' として推論されない
  console.log(user1.name); // 型 'string' として推論される
}

console.log(user2.id); // 型 'number' として推論される
console.log(user2.name); // 型 'string' として推論される
console.log(user2.role); // 型 'editor' として推論される

詳細説明

このサンプルコードでは、以下の点について、解説で紹介した内容をより詳細に記述しています。

  • isAdmin関数の型注釈:

    • この型注釈により、isAdmin関数内でuserオブジェクトの型を絞り込むことができるようになります。
  • if文内の型推論:

    • isAdmin(user1)の条件式がtrueに評価された場合、user1オブジェクトの型は{ role: "admin" }に絞り込まれます。
    • これにより、console.log(user1.id)console.log(user1.name)の型推論結果が、number型とstring型になります。
    • 一方、条件式がfalseに評価された場合、user1オブジェクトの型は絞り込まれません。
  • その他の型推論結果:

    • console.log(user2.id)console.log(user2.name)console.log(user2.role)の型推論結果は、user2オブジェクトのプロパティ型に基づいて行われます。
    • user2オブジェクトのroleプロパティは"editor"であることがわかっているため、console.log(user2.role)の型推論結果はeditor型になります。



ここでは、TypeScriptにおけるユニオン型からの型選択を実現するその他の方法について、いくつか紹介します。

型ガードを用いる方法

型ガードは、条件式を用いて変数の型を絞り込むための構文です。ユニオン型からの型選択にも活用できます。

type UserRole = "admin" | "editor" | "guest";

type User = {
  id: number;
  name: string;
  role: UserRole;
};

function isAdmin(user: User): user is { role: "admin" } {
  if (user.role === "admin") {
    return true;
  } else {
    return false;
  }
}

const user1: User = {
  id: 1,
  name: "Taro",
  role: "admin"
};

const user2: User = {
  id: 2,
  name: "Jiro",
  role: "editor"
};

if (isAdmin(user1)) {
  console.log(user1.id); // 型 'number' として推論される
  console.log(user1.name); // 型 'string' として推論される
} else {
  console.log(user1.id); // 型 'number' として推論されない
  console.log(user1.name); // 型 'string' として推論されない
}

console.log(user2.id); // 型 'number' として推論される
console.log(user2.name); // 型 'string' として推論される
console.log(user2.role); // 型 'editor' として推論される

この例では、isAdmin関数を用いてuserオブジェクトのroleプロパティが"admin"かどうかを判定し、型ガードを行っています。

型ガードの結果に基づいて、if文内のuser1オブジェクトの型を絞り込むことができます。

in演算子を用いる方法

in演算子は、オブジェクトのプロパティが存在するかどうかを確認するために用いる演算子です。ユニオン型からの型選択にも活用できます。

type UserRole = "admin" | "editor" | "guest";

type User = {
  id: number;
  name: string;
  role: UserRole;
};

function isAdmin(user: User): user is { role: "admin" } {
  return "admin" in user;
}

const user1: User = {
  id: 1,
  name: "Taro",
  role: "admin"
};

const user2: User = {
  id: 2,
  name: "Jiro",
  role: "editor"
};

if (isAdmin(user1)) {
  console.log(user1.id); // 型 'number' として推論される
  console.log(user1.name); // 型 'string' として推論される
} else {
  console.log(user1.id); // 型 'number' として推論されない
  console.log(user1.name); // 型 'string' として推論されない
}

console.log(user2.id); // 型 'number' として推論される
console.log(user2.name); // 型 'string' として推論される
console.log(user2.role); // 型 'editor' として推論される

型アサーションを用いる方法

型アサーションは、変数の型を明示的に指定するための構文です。ユニオン型からの型選択にも活用できます。

type UserRole = "admin" | "editor" | "guest";

type User = {
  id: number;
  name: string;
  role: UserRole;
};

function isAdmin(user: User): boolean {
  return user.role === "admin";
}

const user1: User = {
  id: 1,
  name: "Taro",
  role: "admin"
};

const user2: User

typescript


AngularとTypeScriptで「名前が見つかりません」エラーが発生する原因と解決策

原因このエラーの最も一般的な原因は、次のとおりです。スペルミス: 変数、関数、モジュールの名前などにスペルミスがないか確認してください。インポート漏れ: 使用しているモジュールが正しくインポートされていない可能性があります。型定義ファイルの欠損: 使用しているライブラリに型定義ファイルがない場合、このエラーが発生する可能性があります。...


Webpack、Rollup、Parcel... TypeScript 単一ファイルコンパイルツール徹底比較

コードの簡素化: 複数のファイルを単一のファイルにまとめることで、プロジェクトを整理し、コードベースをより扱いやすくすることができます。デプロイの簡素化: 単一のファイルは、複数のファイルよりもデプロイが簡単です。パフォーマンスの向上: 一部のケースでは、単一のファイルにコンパイルすると、パフォーマンスが向上することがあります。...


TypeScript エラー TS2339: "Property 'x' does not exist on type 'Y'" の原因と解決策

より具体的には、以下の状況で発生します。オブジェクト型 'Y' の定義に、プロパティ 'x' が明示的に宣言されていない'Y' 型の変数に、'x' プロパティにアクセスしようとしている例:このエラーを解決するには、以下の方法があります。'x' プロパティにアクセスする前に、'Y' 型の変数を別の型に変換する...


TypeScriptでカスタムグローバルインターフェースを設定する方法

手順プロジェクトフォルダ内に*.d.tsファイルを作成します。ファイル名は自由ですが、一般的にはglobals. d.tsなど分かりやすい名前が推奨されます。ファイル内にインターフェースを定義します。インターフェース名は、グローバルスコープで参照できる名前にしてください。...


Number.isNaN() vs isNaN(): TypeScriptでNaNを安全に判定

Number. isNaN()関数を使うNumber. isNaN()関数は、引数がNaNかどうかを厳密に判断します。これは、ES6で導入された新しい関数であり、より安全で信頼性の高い方法です。isNaN()関数は、引数が数値に変換できるかどうかを確認します。つまり、引数が数値以外の場合はtrueを返し、数値に変換できる場合はfalseを返します。ただし、この関数は引数を暗黙的に数値に変換してしまうため、誤った結果を返す可能性があります。...