TypeScript の高度な型を例を使って解説

TypeScript は、基本型を超える高度な型をいくつか提供しており、より柔軟で強力な型システムを実現します。これらの高度な型は、型の制約を定義および適用するための追加の方法を提供することで、堅牢なアプリケーションの作成に役立ちます。この記事では、これらの高度な型のいくつかを例とともに説明します。

ユニオン型

ユニオン型を使用すると、変数を複数の型のいずれかにすることができます。これは、値が複数の型になる可能性があるが、実際の型に基づいて適切に処理する必要がある場合に役立ちます。

// Union type example

function formatValue(value: string | number): string {
  if (typeof value === 'string') {
    return `String: ${value}`;
  } else {
    return `Number: ${value.toFixed(2)}`;
  }
}

console.log(formatValue("Hello"));
console.log(formatValue(123.456));

この例では、`formatValue` 関数は文字列または数値のいずれかを受け入れ、それに応じて値をフォーマットします。

交差点の種類

交差型は複数の型を 1 つに結合します。交差型のオブジェクトは結合された型のすべてのプロパティを持ちます。これは複数の型を一緒に合成する場合に便利です。

// Intersection type example

interface Person {
  name: string;
  age: number;
}

interface Contact {
  email: string;
  phone: string;
}

type Employee = Person & Contact;

const employee: Employee = {
  name: "John Doe",
  age: 30,
  email: "john.doe@example.com",
  phone: "123-456-7890"
};

console.log(employee);

ここで、`Employee` 型は `Person` と `Contact` の積集合であり、両方のインターフェースのプロパティが含まれていることを意味します。

リテラル型

リテラル型は、変数が保持できる正確な値を指定します。これは、特定の値のみが許可されるようにする場合に特に便利です。

// Literal type example

type Direction = "up" | "down" | "left" | "right";

function move(direction: Direction): void {
  console.log(`Moving ${direction}`);
}

move("up");    // Valid
move("down");  // Valid
// move("side"); // Error: Argument of type '"side"' is not assignable to parameter of type 'Direction'.

ここでの `Direction` タイプは 4 つの特定の文字列値に制限されており、これらの方向のみが `move` 関数で使用できるようになります。

タプル型

タプル型は、各要素が異なる型を持つことができる固定数の要素を持つ配列を表します。タプルは、異種の項目の固定サイズのコレクションを表すのに役立ちます。

// Tuple type example

let user: [string, number] = ["Alice", 30];

console.log(user[0]); // "Alice"
console.log(user[1]); // 30

// user = [30, "Alice"]; // Error: Type 'number' is not assignable to type 'string'.

`user` タプルは文字列とそれに続く数字で定義され、この構造を維持する必要があります。

条件型

条件型を使用すると、条件に基づいて型を決定できます。条件に基づいて 1 つの型または別の型を選択する方法が提供されます。

// Conditional type example

type IsString = T extends string ? "Yes" : "No";

type Test1 = IsString;  // "Yes"
type Test2 = IsString;  // "No"

この例では、`IsString` 型は型 `T` が文字列かどうかをチェックします。文字列の場合は `"Yes"` を返し、そうでない場合は `"No"` を返します。

マッピングされたタイプ

マップされた型を使用すると、既存の型のプロパティを変換して新しい型を作成できます。これは、既存の型を変更または拡張する場合に便利です。

// Mapped type example

type ReadonlyPerson = {
  readonly [K in keyof Person]: Person[K];
};

const readonlyPerson: ReadonlyPerson = {
  name: "Alice",
  age: 30
};

// readonlyPerson.age = 31; // Error: Cannot assign to 'age' because it is a read-only property.

`ReadonlyPerson` 型は、すべてのプロパティを読み取り専用にすることで `Person` 型を変換します。

結論

TypeScript の高度な型は、複雑な型要件を定義および管理するための強力なツールを提供します。 ユニオン、インターセクション、リテラル、タプル、条件付き、およびマップされた型を利用することで、開発者はより堅牢で保守しやすいアプリケーションを作成できます。 これらの型を効果的に理解して適用すると、TypeScript コードの型の安全性と柔軟性が大幅に向上します。