【TypeScript】型システムの魔法入門

こんにちは、皆さん！

私は魔法が使えるお嬢様、シャーロット・ムラタと申します。


今日は、TypeScriptの型システムについて、私の魔法の力を使って解説いたします。魔法のように見えるかもしれませんが、実はTypeScriptの強力な機能を駆使しているだけです。

さあ、魔法の世界へご案内しましょう！

# ジェネリクスの魔法
まずはジェネリクスについてです。

ジェネリクスは、私たちの魔法の杖のようなものです。どんな形にも変えられるこの杖を使って、さまざまな型に適用できる汎用的な関数やクラスを作り出すことができます。

## ジェネリック関数
たとえば、魔法のポーションを作る関数があるとしましょう。このポーションは、どんな材料（型）を使っても作ることができます。ジェネリクスを使うと、こんな感じになります。

```typescript
function createPotion<T>(ingredient: T): T {
    return ingredient;
}

// 使用例
const healthPotion = createPotion('Herbs');
const manaPotion = createPotion(100);
```
ここでは、Tという魔法のシンボルを使っています。このシンボルはポーションの材料が何であってもそれに対応する型となります。createPotion関数は、与えられた材料をそのまま返します。つまり、ポーションを作るという魔法を実行するのです。

## ジェネリッククラス
次に、魔法の宝石箱を考えてみましょう。


この宝石箱には、どんな宝石（型）でも保管できます。ジェネリッククラスを使うと、以下のように定義できます。

```typescript
class MagicBox<T> {
    private item: T;

    constructor(item: T) {
        this.item = item;
    }

    open(): T {
        return this.item;
    }
}

// 使用例
const jewelBox = new MagicBox('Ruby');
console.log(jewelBox.open()); // Ruby

const spellBox = new MagicBox({ spell: 'Fireball' });
console.log(spellBox.open()); // { spell: 'Fireball' };
```
MagicBoxクラスは、任意の型Tを扱うことができる魔法の箱です。itemはその型のアイテムであり、openメソッドで中身を取り出すことができます。この魔法の箱は、どんな宝石でも安全に保管してくれます。

# ユニオン型の魔法
次にユニオン型についてです。

ユニオン型は、私たちの魔法の書のようなものです。この書には、複数の呪文（型）が記されています。どの呪文を使うかは、その時々の状況に応じて決まります。

## 基本的なユニオン型
例えば、魔法の書に書かれた呪文が「火の玉」や「氷の刃」だったとしましょう。ユニオン型を使うと、次のように表現できます。

```typescript
type Spell = 'Fireball' | 'Iceblade';

function castSpell(spell: Spell): void {
    console.log(`Casting ${spell} spell!`);
}

// 使用例
castSpell('Fireball'); // Casting Fireball spell!
castSpell('Iceblade'); // Casting Iceblade spell!
```

ここでは、Spellというユニオン型を定義しました。この型は、「Fireball」または「Iceblade」のいずれかであることを示しています。castSpell関数は、その呪文をキャストするという魔法を実行します。

## 複合的なユニオン型

もう少し複雑な魔法を見てみましょう。

例えば、魔法のアイテムが「杖」か「巻物」か「ポーション」で、それぞれ異なる特性を持っているとします。ユニオン型を使って、これらのアイテムを1つの型で扱うことができます。

```typescript
type Wand = { type: 'wand'; power: number };
type Scroll = { type: 'scroll'; spells: string[] };
type Potion = { type: 'potion'; effect: string };

type MagicItem = Wand | Scroll | Potion;

function identifyItem(item: MagicItem): void {
    switch (item.type) {
        case 'wand':
            console.log(`A wand with power level: ${item.power}`);
            break;
        case 'scroll':
            console.log(`A scroll containing spells: ${item.spells.join(', ')}`);
            break;
        case 'potion':
            console.log(`A potion with effect: ${item.effect}`);
            break;
    }
}

// 使用例
identifyItem({ type: 'wand', power: 10 }); // A wand with power level: 10
identifyItem({ type: 'scroll', spells: ['Fireball', 'Heal'] }); // A scroll containing spells: Fireball, Heal
identifyItem({ type: 'potion', effect: 'Invisibility' }); // A potion with effect: Invisibility
```

ここでは、MagicItemというユニオン型を定義しました。

この型は、「wand」か「scroll」か「potion」のいずれかであることを示しています。identifyItem関数は、アイテムの種類に応じて適切なメッセージを表示します。この魔法の書を使うことで、どんなアイテムも簡単に識別することができます。

# 上級魔法（高度なユースケース）

ではジェネリクスとユニオン型を組み合わせてみましょう。


例えば、魔法の市場で売られているアイテムを管理するシステムを作るとします。この市場には、さまざまな種類のアイテムが並んでいます。

```typescript
type Item<T> = { id: number; name: string; data: T };

type WeaponData = { damage: number };
type ArmorData = { defense: number };
type PotionData = { effect: string };

type Weapon = Item<WeaponData>;
type Armor = Item<ArmorData>;
type Potion = Item<PotionData>;

type MarketItem = Weapon | Armor | Potion;

function describeItem(item: MarketItem): void {
    console.log(`Item ID: ${item.id}, Name: ${item.name}`);
    if ('damage' in item.data) {
        console.log(`This weapon deals ${item.data.damage} damage.`);
    } else if ('defense' in item.data) {
        console.log(`This armor provides ${item.data.defense} defense.`);
    } else if ('effect' in item.data) {
        console.log(`This potion has the effect of ${item.data.effect}.`);
    }
}

// 使用例
const sword: Weapon = { id: 1, name: 'Excalibur', data: { damage: 50 } };
const shield: Armor = { id: 2, name: 'Aegis', data: { defense: 20 } };
const elixir: Potion = { id: 3, name: 'Elixir of Life', data: { effect: 'Healing' } };

describeItem(sword);
describeItem(shield);
describeItem(elixir);
```

ここでは、Item<T>というジェネリック型を定義し、WeaponData、ArmorData、PotionDataという具体的なデータ型を持つアイテムのユニオン型を作りました。describeItem関数は、アイテムのデータに応じて適切なメッセージを表示します。

この魔法の市場システムを使うことで、どんなアイテムも簡単に管理できます。

# おわりに

いかがでしたか？

魔法のように見えるTypeScriptの高度な型システムを使いこなすことで、コードの安全性と再利用性を高めることができます。ジェネリクスとユニオン型は、まさに魔法の杖と書のような存在です。これらの魔法を使って、あなたのプログラムをさらに強力なものにしましょう。

それでは、次回の魔法の授業でお会いしましょう。

シャーロット・ムラタより

※シャーロット・ムラタのイメージ（作：ChatGPT）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1953297/a7fd378a-039d-913c-8407-9091275ac480.png)