メインコンテンツにスキップ
モジュール 2 · レッスン 2 / 2コース内の 4/14~15 min
モジュールのレッスン (2/2)

結合と縮小

現実の世界では、変数と API 応答は常に 1 つの固定型を持つとは限りません。 TypeScript は、変動性を処理するための Union Types と、実行時に安全に動作するための Type Narrowing を提供します。


ユニオンの種類

共用体型を使用すると、変数がさまざまな型の値を受け入れることができます。これは、垂直バー (|) 記号を使用して表されます。

TS
let result: number | string;
result = 42; // Valid
result = 'Error 404'; // Valid

ただし、共用体型を使用する場合、いずれかの型にのみ属するメソッドを直接呼び出すことはできません (たとえば、変数が number である可能性がある場合、.toUpperCase() を呼び出すことはできません)。まずタイプを「絞り込む」必要があります。


タイプの絞り込み

型の絞り込み は、TypeScript が制御フロー構造 (if や switch など) を分析して、実行時に変数のより具体的な型を推測するプロセスです。

絞り込みを実行するにはいくつかの方法があります。

1. typeof 演算子

プリミティブ型を区別するのに最適です。

TS
function printLength(value: string | number) {
  if (typeof value === 'string') {
    // TypeScript knows 'value' is a string here
    console.log(value.length);
  } else {
    // TypeScript knows 'value' is a number here
    console.log(value.toFixed(2));
  }
}

2. in 演算子

特定のプロパティがオブジェクトに存在するかどうかを確認するために使用されます。

TS
interface Fish {
  swim: () => void;
}
interface Bird {
  fly: () => void;
}

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    animal.swim(); // Narrowing to Fish
  } else {
    animal.fly(); // Narrowing to Bird
  }
}

差別的な組合

Discriminated Unions パターンは、一意の リテラル値 (discriminator と呼ばれる) を持つ共通のプロパティを共有するオブジェクトの作成で構成されます。 TypeScript はこの識別子を認識し、条件ブロック内の型を自動的に絞り込みます。

TS
interface SuccessResponse {
  status: 'success'; // Literal discriminator
  data: string;
}

interface ErrorResponse {
  status: 'error'; // Literal discriminator
  errorMessage: string;
}

type ApiResponse = SuccessResponse | ErrorResponse;

function handleResponse(response: ApiResponse) {
  if (response.status === 'success') {
    console.log('Data received:', response.data);
  } else {
    console.error('An error occurred:', response.errorMessage);
  }
}

自分で試してみてください

演習 1: 共用体の型

運動#ts.m2.l2.e1
試行回数: 0読み込み中…

id という名前の変数を宣言します。この変数には数値または文字列を指定できます。まず数値 101 で初期化し、次に文字列値 'USER-101' を割り当てます。

エディターを読み込み中…
ヒントを表示

| を使用します。 let 変数宣言で数値と文字列を結合する演算子。

3 回の試行後に解決策が利用可能になります

演習 2: 基本的な型の絞り込み

運動#ts.m2.l2.e2
試行回数: 0読み込み中…

文字列または数値型のパラメーター入力を受け入れる formatInput という名前の関数を作成します。入力が文字列の場合は、大文字に変換された入力を返します。数値の場合は、入力を 2 倍して返します。型を明示的に指定します。

エディターを読み込み中…
ヒントを表示

if ブロック内で typeof input === 'string' を使用して動作を分岐させます。

3 回の試行後に解決策が利用可能になります

演習 3: 「in」による絞り込み

運動#ts.m2.l2.e3
試行回数: 0読み込み中…

Car (drive メソッドを使用) と Boat (sail メソッドを使用) という 2 つのインターフェイスがある場合、Car または Boat タイプのパラメーター車両を受け入れる moveVehicle という名前の関数を作成します。車両に drive プロパティがある場合は、drive() メソッドを実行します。それ以外の場合は、sail() メソッドを実行します。

エディターを読み込み中…
ヒントを表示

インターフェイスを絞り込むには、車両内で「drive」という形式で in 演算子を使用します。

3 回の試行後に解決策が利用可能になります

演習 4: 判別された共用体形状

運動#ts.m2.l2.e4
試行回数: 0読み込み中…

Circle と Square の 2 つのタイプを結合したタイプ Shape を定義します。 Circle には、kind プロパティが 'circle' (リテラル値) に設定され、radius (数値) が設定されています。 Square には、kind プロパティが 'square' (リテラル値) と Side (数値) に設定されています。次に、Shape 型の形状を受け入れ、面積を数値として返す関数 getArea を作成します (円の場合は Math.PI * 半径 * 半径、正方形の場合は辺 * 辺)。

エディターを読み込み中…
ヒントを表示

getArea 内でshape.kind === 'circle'を使用してタイプを識別し、正しい面積を計算します。

3 回の試行後に解決策が利用可能になります