【TypeScript】初～中級者 12の機能とテクニック【リトアニアのクリスマス・イブ】


# 00. はじめに：なぜTypeScriptとリトアニア？

リトアニアでは、クリスマス・イブの晩餐に12種ほどの料理を用意する習慣があります。
<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>と呼ばれるこの伝統的な晩餐には、肉や乳製品はご法度というルールがあります[^リトアニアのクリスマス・イブ]。

[^リトアニアのクリスマス・イブ]: 12という数字は十二使徒か1年の月の数に由来し、料理は卵や動物油も避けるそう：
  [KŪČIOS - Kauno tautinės kultūros centras](https://ktkc.lt/kucios/)
  [Kūčios – Vikipedija](https://lt.wikipedia.org/wiki/K%C5%AB%C4%8Dios)
  [クリスマス・イブの12品の晩餐 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AA%E3%82%B9%E3%83%9E%E3%82%B9%E3%83%BB%E3%82%A4%E3%83%96%E3%81%AE12%E5%93%81%E3%81%AE%E6%99%A9%E9%A4%90)
  [リトアニア便り » クリスマス・イブ「クーチョス」](http://from-lt.jlfa.jp.net/archives/377)

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/ac0ef3f0-b22e-6321-cbde-a17c25835167.png" alt="クリスマス・イブの晩餐（筆者宅／2023）" width="50%" /> |
| :-: |
| **クリスマス・イブの晩餐（筆者宅／2023）** <br/> ~~もはやたくさんの料理をつくることが目的で、肉等禁止のルールは守っていない~~ |

本記事は、クリスマスまでをカウントするアドベントカレンダーの、24日目の記事です。
昨日23日には、 @Ai-Fukiharu さんが「Miro」について語ってくれました：

https://qiita.com/Ai-Fukiharu/items/d4ecf0e3dc0a0fa78d28

本記事では、「アドベントカレンダー24日目には特別な味付けを」という思いから、
~~TypeScriptテクニックという多くの人が読んでくれそうな内容に、
筆者のルーツの１つであるリトアニアの味をつけて新規性を加えるため、~~
**リトアニアの料理や伝統的な要素になぞらえながら、
TypeScriptの12種の機能と、それらに関連するテクニックを解説します。**

:::note info
筆者はフロントエンドエンジニアとしてTypeScriptを2年ほど使用しています。
本記事の対象者はTypeScript初級者～中級者と幅広く想定しています。
:::

聖なる夜に並ぶ料理のように、各章が皆様の学びの糧となりますように。
それでは、伝統を重んじる人々の目に留まらないことを祈りつつ[^バルト・ネオペイガニズム]、ご紹介します。

[^バルト・ネオペイガニズム]: リトアニアを含めたバルト諸国では、キリスト教が広まる以前に、バルト民族独自の多神教信仰があった：
  [Baltic Religion - Lietuvos nacionalinis kultūros centras](https://www.lnkc.lt/eknygos/roots/node16.html)
  [リトアニアの宗教 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%88%E3%82%A2%E3%83%8B%E3%82%A2%E3%81%AE%E5%AE%97%E6%95%99)
  キリスト教が広まってからしばらくたったあとに、過去の信仰を重んじ、それを復興する「バルト・ネオペイガニズム」と呼ばれる運動があった：
    [Baltų religija - Romuva](https://romuva.lt/pasauleziura/baltureligija/)
    [バルト・ネオペイガニズム - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%AB%E3%83%88%E3%83%BB%E3%83%8D%E3%82%AA%E3%83%9A%E3%82%A4%E3%82%AC%E3%83%8B%E3%82%BA%E3%83%A0)
  本記事では、<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>（リトアニアの冬の晩餐における習慣）が、キリスト教のみにおけるものと扱っているように見えるが、起源はキリスト教が広まる以前にあると考えられている：
  [Kas yra Kūčios? | Lakrima laidojimo namai](https://ve.lt/gyvenimas/kuciu-vakarienei-tinka-ir-silke-ir-kalmarai)
  [Kūčios - viena svarbiausių lietuvių šeimos švenčių](https://ve.lt/gyvenimas/kuciu-vakarienei-tinka-ir-silke-ir-kalmarai)
  [Lithuanian Kūčios: awe-inspiring rites in anticipation of Christmas](https://lithuaniatribune.com/lithuanian-kucios-traditions/)
  そのため、伝統を重んじる（ネオ）ペイガンの反感を買うことを恐れて、序文を足早に切り上げている。

:::note warn
本記事ではリトアニアの文化や宗教、伝統に触れますが、決してこれらを軽視したり価値判断したりする意図はありません。
:::










# 01. リテラル型：Kalėdaitis

<ruby><rb>Kalėdaitis</rb><rt>カレダイティス</rt></ruby>は、<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>で欠かせない聖なるウエハースです[^聖なるウエハース]。
水と小麦粉でつくられ、伝統的な形や模様を持ち、淡白な味を守り続けています[^水と小麦粉]。

[^聖なるウエハース]: <ruby><rb>Kalėdaitis</rb><rt>カレダイティス</rt></ruby>はイエス・キリストの体を象徴し、<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>の晩餐で家族が分かち合う聖なる存在とされている：
  [kalėdaitis - Visuotinė lietuvių enciklopedija](https://www.vle.lt/straipsnis/kaledaitis/)
  呼び方は<ruby><rb>Kalėdaitis</rb><rt>カレダイティス</rt></ruby>の他にもたくさんあり、我が家では<ruby><rb>Plotkelės</rb><rt>プロトキャーレス</rt></ruby>と呼んでいる。

[^水と小麦粉]: つくるのはかなり大変みたい：
  [Kuklusis kalėdaitis atkeliauja iš vienuolynų | Šiaulių kraštas](https://www.skrastas.lt/aktualijos/skaitytojai-kreipiasi/kuklusis-kaledaitis-atkeliauja-is-vienuolynu)

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/951a88e1-90b2-32dc-c989-e326597d10b8.png" alt="3種のKalėdaitis" width="80%" /> |
| :-: |
| **3種の<ruby><rb>Kalėdaitis</rb><rt>カレダイティス</rt></ruby>**[^Kalėdaitis]<br />我が家もイブに頂いている |

[^Kalėdaitis]: 画像は英Wikipediaから拝借：
  [Christmas wafer - Wikipedia](https://en.wikipedia.org/wiki/Christmas_wafer)

リテラル型（Literal Types）は、「より具体的な型」です。
主に `string`・`number`・`boolean` 型の特定の値を指定して、
それらのみを受け付けるようにすることができるものです[^リテラル型]。

[^リテラル型]: リテラル型についての説明は公式ドキュメントを参考にしている：
  [TypeScript: Handbook - Literal Types](https://www.typescriptlang.org/docs/handbook/literal-types.html)

たとえば、以下のように文字列変数を `let` で宣言すると、`string` 型になります：

```ts
let size = "small";
// let size: string
```

しかし、`const` で同様に宣言すると、その型はリテラル型となり、
ここでは指定した文字列が型になります：

```ts
const size = "small";
// const size: "small"
```

`const` で宣言された変数は値を変更できません[^constの再代入]。
そのため、TypeScriptは変数がその初期値以外の値を取らないと判断し、
初期値そのものを表すリテラル型を割り当てます。

一方、`let` は再代入が可能なため、将来的に値が変わる可能性を踏まえ、
`string` や `number` などの、より一般的な型に拡張します。
これは「Widening」と呼ばれるTypeScriptの推論ルールによるものです[^Widening]。

[^constの再代入]: 公式ドキュメントを参考にした表現とした：
  [TypeScript: Documentation - Variable Declaration](https://www.typescriptlang.org/docs/handbook/variable-declarations.html#const-declarations)
  参照する値が不変であるわけではない点に注意。
  たとえば、同じ名前のオブジェクトを再度宣言することはできないが、プロパティの値を変更することはできる。

[^Widening]: 実は公式ドキュメントでは「Widening」と呼ばれていないが、公式サイトのプレイグラウンドに記載があるので、公式用語ではないが一般的に使われている語彙であると考えてよさそう：
  [TypeScript Playground - Type Widening and Narrowing](https://www.typescriptlang.org/play/?#example/type-widening-and-narrowing)

たとえば、関数のパラメータにリテラル型を用いると、許容する値を限定できるため、
想定外の文字列や数値が渡されることをコンパイル段階で防げます。
以下は、<ruby><rb>Kalėdaitis</rb><rt>カレダイティス</rt></ruby>が厳密な伝統的手順や形状を守って作られるのと同じように、
コード上でも予期せぬ値の混入を防ぐ「型的な安全性」を確保する手法です：

```ts
function setSize(size: "small" | "medium" | "large") {
  console.log(`Selected size: ${size}`);
}

setSize("small");       // OK
setSize("medium");      // OK
setSize("extra-large"); // コンパイルエラー：
// Argument of type '"extra-large"' is not assignable to parameter of type '"small" | "medium" | "large"'.
```

この `size` は、パイプ記号（`|`）で繋げて書いた3つのリテラル型の、いずれかの型です。
複数の型のいずれかであることを示す型を、ユニオン型 (Union Types)と呼びます[^ユニオン型]。
`size` には、型注釈した `"small"` や `"medium"` は代入できますが、
`"extra-large"` はそうでないため代入できず、コンパイルエラーとなります。

[^ユニオン型]: ユニオン型の詳しい説明については公式ドキュメントを参照されたい：
  [TypeScript: Handbook - Unions and Intersection Types](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html)


## Discriminated Union で Narrowing

リテラル型は、判別可能なユニオン型と組み合わせると、より強力に型を表現できます。
「判別可能なユニオン型（Discriminated Union）」とは、
共通の識別子となるプロパティを持つ複数の型を、ユニオン型で組み合わせたものです[^判別可能なユニオン型]。

[^判別可能なユニオン型]: Discriminated Unionは公式ドキュメントで、その邦訳である判別可能なユニオン型は有用な解説として知られるオープンソースドキュメント「TypeScript Deep Dive」で用いられている：
  [TypeScript: Documentation - Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions)
  [判別可能なUnion型 | TypeScript Deep Dive 日本語版](https://typescript-jp.gitbook.io/deep-dive/type-system/discriminated-unions)

以下はウェハーの種類 `kind` を識別子として、それが<ruby><rb>Kalėdaitis</rb><rt>カレダイティス</rt></ruby>かそれ以外かで
その質感 `texture` が異なるという、判別可能なユニオン型の例です：

```ts
type WaferType = 
  | { kind: "kalėdaitis"; texture: "thin" }
  | { kind: "other"; texture: "thick" };
```

この共通プロパティがリテラル型であるため、型の絞り込み（Narrowing）ができます[^Narrowing]。

[^Narrowing]: 公式ドキュメントにWideningについては立項されていないが、Narrowingについてはある：
  [TypeScript: Documentation - Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html)
  `in` や `typeof` による型ガード（Type Guard）のためのNarrowingもある。

これにより、単なる文字列や数値の制約にとどまらず、
複数の異なる「形」を持つオブジェクト群をひとまとめの型で管理し、
条件分岐に応じて明確に型を切り替えることが可能となります。

上記の判別可能なユニオン型 `WaferType` を用いて条件分岐する例を示します：

```ts
function describeWafer(wafer: WaferType) {
  if (wafer.kind === "kalėdaitis") {
    // waferはここでkind: "kalėdaitis"と判定され、textureは"thin"とわかる
    console.log("This is a traditional kalėdaitis.");
  } else {
    console.log("This is some other wafer.");
  }
}
```

`kind` が `"kalėdaitis"` であると判別された箇所では、
`wafer` が `{ kind: "kalėdaitis"; texture: "thin" }` 型に絞り込まれます。
これにより、`wafer.texture` へ安全にアクセスでき、
「<ruby><rb>Kalėdaitis</rb><rt>カレダイティス</rt></ruby>は必ず薄い（thin）」ということが、型レベルで保証されます。

このように、リテラル型とユニオン型は組み合わせることで
より複雑なロジックを型安全かつ明確なコードで表現でき、
予期せぬ値や状態が紛れ込むのを防ぐことができます。










# 02. constアサーション：Kūčiukai

<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>で欠かせないのが、小さく固く焼き上げられた伝統菓子「<ruby><rb>Kūčiukai</rb><rt>クチューカイ</rt></ruby>」です[^クチューカイ]。
リトアニア政府観光局によれば「クリスマス・イブのクッキー」を意味するそうで、
祖母から頂く<ruby><rb>Kūčiukai</rb><rt>クチューカイ</rt></ruby>は、まるで石のように硬く、
リトアニアから日本への長い旅路を経ても、決して形が崩れることはありません。

[^クチューカイ]: Kūčiukaiの発音について、文章では「クチューカイ」と「クーチュカイ」の2通りがよく見られるが、私の耳に従い「クチューカイ」を採用した。

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/06081a77-3f34-1cbf-2adb-fa69c1146054.jpeg" alt="2種類のKūčiukai（筆者宅／2023）" width="60%" /> |
| :-: |
| **2種類のKūčiukai（筆者宅／2023）**<br />上のKūčiukaiが本当に硬く、父の歯を欠けさせた実績がある |

TypeScriptの const アサーション（Const Assertion）は
`as const` という書き方で、この「不変性」と「安定性」を型レベルで表現します。
これを使うと、オブジェクトや配列がリテラル型として推論されるとともに、
要素やプロパティが `readonly` という固定的な値となり、変更を型レベルで防げます[^ConstAssertion]。

[^ConstAssertion]: constアサーションはTypeScript 3.4で追加された：
  [TypeScript: Documentation - TypeScript 3.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions)

`as const` を使わない場合は、要素やプロパティの型は推論されます：

```ts
const sizes = ["small", "medium", "large"];
// const sizes: string[]

sizes[0] = "tiny"; // OK
```

`as const` を使う場合は、要素やプロパティがリテラル型かつ `readonly` になります：

```ts
const sizes = ["small", "medium", "large"] as const;
// const sizes: readonly ["small", "medium", "large"]

sizes[0] = "tiny"; // コンパイルエラー：
// Cannot assign to '0' because it is a read-only property.
```

形が変わらない<ruby><rb>Kūčiukai</rb><rt>クチューカイ</rt></ruby>のように、
最初に定めた要素以外が紛れ込むことはありません。

また、以下の通りオブジェクトでも同様で、
内包するオブジェクトや配列も再帰的に `readonly` 化されます：

```ts
const lithuania = {
  country: "Lithuania",
  sweets: {
    kūčiukai: {
      ingredients: ["wheat flour", "poppy seeds", "water", "yeast"],
      texture: "hard",
      origin: "traditional"
    }
  },
  festival: "Kūčios"
} as const;
// const lithuania: {
//   readonly country: "Lithuania";
//   readonly sweets: {
//     readonly kūčiukai: {
//       readonly ingredients: readonly ["wheat flour", "poppy seeds", "water", "yeast"];
//       readonly texture: "hard";
//       readonly origin: "traditional";
//     };
//   };
//   readonly festival: "Kūčios";
// }

lithuania.sweets.kūčiukai.ingredients[0] = "rice flour";
// エラー：Type 'string' is not assignable to type '"wheat flour"'.

lithuania.sweets.kūčiukai.texture = "soft";
// エラー：Cannot assign to 'texture' because it is a read-only property.
```

`as const` を付けたことで、
`ingredients` 配列の各要素はリテラル型として推論され、
`readonly` が再帰的に適用されます。
これにより、後から要素を変更しようとしてもコンパイルエラーとなるため、
コード上でも定義した値や構造を揺るぎないものとして扱うことができます。











# 03. satisfies：Aguonų pienas

<ruby><rb>Aguonų</rb><rt>アグオヌ</rt></ruby> <ruby><rb>pienas</rb><rt>ピエナス</rt></ruby>は、<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>の食卓によく登場する、
ケシの実（Poppy Seeds）から作られる、白くてまろやかな飲み物です[^AguonųPienas]。

[^AguonųPienas]: <ruby><rb>Aguonų</rb><rt>アグオヌ</rt></ruby> <ruby><rb>pienas</rb><rt>ピエナス</rt></ruby>はハチミツや塩を使って味を調えることもあるそう：
  [Aguonų pienas](https://www.sezoninevirtuve.lt/aguonu-pienas/)

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/16b24e22-7a61-91ad-60a1-6ba0d80a87c4.png" alt="黒いAguonos（ケシの実）とAguonų pienas" width="100%" /> |
| :-: |
| **黒い<ruby><rb>Aguonos</rb><rt>アグオノス</rt></ruby>（ケシの実）と<ruby><rb>Aguonų</rb><rt>アグオヌ</rt></ruby> <ruby><rb>pienas</rb><rt>ピエナス</rt></ruby>**[^アグオヌピエナスの画像] |

[^アグオヌピエナスの画像]: 画像はリトアニアのニュースサイト「tv3.lt」から拝借：
  [Išdavė skaniausią aguonų pieno receptą: užsirašykite Kūčioms | tv3.lt](https://www.tv3.lt/naujiena/gyvenimas/isdave-skaniausia-aguonu-pieno-recepta-uzsirasykite-kucioms-n1203939)

TypeScript 4.9にて、`satisfies` という演算子が追加されました[^4.9]。
値が指定した型をきちんと満たしているかをコンパイル時に保証するものです。

[^4.9]: TypeScript 4.9の新機能については以下を参照されたい：
  [Announcing TypeScript 4.9 - TypeScript](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/)
  [TypeScript: Documentation - TypeScript 4.9](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html)

以下で、`satisfies` 単体の基本的な活用例を示すために、ドリンクレシピを例に、
`satisfies` が型の整合性チェックにどう役立つか確認してみましょう：

```ts
type DrinkRecipe = {
  name: string;
  ingredients: string[];
};

// OK
const aguonuPienas = {
  name: "Aguonų pienas",
  ingredients: ["poppy seeds", "water"]
} satisfies DrinkRecipe;

// エラー
const errorDrink = {
  name: "Error",
  ingredients: "water", // 期待された文字列リストでないため、以下のエラーとなる：
  // Type 'string' is not assignable to type 'string[]'.
  // The expected type comes from property 'ingredients' which is declared here on type 'DrinkRecipe'
} satisfies DrinkRecipe;
```

`ingredients` を文字列で書いたため指定した `DrinkRecipe` 型を満たさない
値 `errorDrink` は、実際にその部分でコンパイルエラーとなっています。



## 型注釈との違い

`satisfies` 演算子と型注釈（Type Anotation）は、
どちらも値が型を満たすことを示すものですが、異なる役割と挙動があります。
以下に、その違いが分かりやすい例を提示します：

```ts
type Flavor = "vanilla" | "chocolate";
type Dessert = {
  name: string;
  flavor: Flavor;
};

// この関数はflavorがリテラル型"vanilla"であることを要求する
function acceptVanillaOnly(d: { flavor: "vanilla" }) { /* ... */ }

// --- 型注釈の場合 ---
const dessertA: Dessert = {
  name: "Aguonų pienas",
  flavor: "vanilla"
};

// dessertA.flavorはFlavor型("vanilla" | "chocolate")であり、リテラル型"vanilla"ではない
acceptVanillaOnly(dessertA);
// エラー：dessertA.flavorは"vanilla"リテラルではないため、引数に合わない
// Argument of type 'Dessert' is not assignable to parameter of type '{ flavor: "vanilla"; }'.
//   Types of property 'flavor' are incompatible.
//      Type 'Flavor' is not assignable to type '"vanilla"'.
//        Type '"chocolate"' is not assignable to type '"vanilla"'.

// --- satisfiesの場合 ---
const dessertB = {
  name: "Aguonų pienas",
  flavor: "vanilla"
} satisfies Dessert;

// dessertB.flavorは"vanilla"リテラル型として推論され続ける
acceptVanillaOnly(dessertB); 
// OK：dessertB.flavorは厳密に"vanilla"であり、要求を満たす
```

型注釈は値を特定の型にピッタリと合わせるものです。
`satisfies` は型適合を確認するだけで、値自体の厳密な型情報は消しません。
そのため、型注釈よりも、細い型安全性と柔軟な推論結果を両立できます。



## as const satisfies

<ruby><rb>Aguonų</rb><rt>アグオヌ</rt></ruby> <ruby><rb>pienas</rb><rt>ピエナス</rt></ruby>は、<ruby><rb>Kūčiukai</rb><rt>クチューカイ</rt></ruby>を浸して頂くことがあります：

https://x.com/Lithuaniajp/status/1738035651856269737

`satisfies` 演算子も、`as const` と組み合わせて使うことが少なくありません。
`as const satisfies 型` という記法で、
「不変かつ厳密な値の集合」（`as const`）と「その集合が正しい型規格を満たしているか」（`satisfies`）を同時に保証できるようになります。
つまり、以下のように各種設定ファイルなどで定数をつくるときに便利です：

```ts
export const colors = {
   Kūčiukai: "#ffffff",
   Aguonų: ["#000000", "#ffffff"], // 配列でもOK
   // satisfiesによる型安全性の確認
   Onion: "red",                   // satisfiesにより型を保証されているためエラー：
   // Type '"red"' is not assignable to type '`#${string}`'.
} as const satisfies {
  [key: string]: `#${string}` | (`#${string}`)[];
};

// as constによる値固定化の確認
colors.Aguonų = "#ff0000"; // as constによりradonlyであるためエラー：
// Cannot assign to 'Aguonų' because it is a read-only property.

// 配列が引数なければならない関数
function mapArray(arr: string[]) { return arr.map((v) => v) }

// as constによるwidening阻止の確認
mapArray(colors.Kūčiukai); // Kūčiukaiは配列でないため、当然エラー
mapArray(colors.Aguonų);   // as constによりwideningされないのでOK（型注釈などで実装した場合はエラーになる）
```










# 04. ジェネリクス：Silkė su svogūnais

<ruby><rb>Silkė</rb><rt>シルケ</rt></ruby> <ruby><rb>su</rb><rt>ス</rt></ruby> <ruby><rb>svogūnais</rb><rt>スヴォグーナイス</rt></ruby>は、<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>で欠かせない伝統のニシン料理です。
キリスト教文化の影響で、<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>では肉が避けられるようになり、
代わりに魚が定着した[^キリスト教の普及による食文化の変化]おかげで、特にこの料理は多くの家庭で愛されています。

[^キリスト教の普及による食文化の変化]: キリスト教普及前は肉料理もOKで、料理の種類も9種類だったとのこと：
  [Lietuviškos Kūčių tradicijos: valgiai ir jų simbolika | MENIU](https://www.meniu.lt/naujiena/lietuviskos-kuciu-tradicijos-valgiai-ir-ju-simbolika)
  [Žuvis Kūčioms: paprasti receptai tradiciniam Kūčių stalui](https://derlingas.lt/receptai/zuvis-kucioms-paprasti-ir-laiko-patikrinti-receptai-tradiciniam-stalui)

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/d9702f02-74e5-16dd-7655-bd66bdfaea53.png" alt="Silkė su svogūnais（ニシンの玉ねぎ添え）" width="80%" /> |
| :-: |
| **<ruby><rb>Silkė</rb><rt>シルケ</rt></ruby> <ruby><rb>su</rb><rt>ス</rt></ruby> <ruby><rb>svogūnais</rb><rt>スヴォグーナイス</rt></ruby>（ニシンの玉ねぎ添え）**[^SilkėSuSvogūnais]<br />黄色いのはじゃがいも |

[^SilkėSuSvogūnais]: 画像はリトアニアの園芸・農業サイトDerlingas.ltから拝借：
  [Silkė su svogūnais – šventinio Kūčių stalo karalienė | Derlingas.lt](https://derlingas.lt/receptai/silke-su-svogunais-sventinio-kuciu-stalo-karaliene)
  ニシンはかつてのリトアニアでは珍味で、調理されることはなかったそう。

ジェネリクス（Generics）もまた、TypeScriptを語るうえで欠かせない代表的な機能です。
ジェネリクスを用いると、一度定義した型パターンを異なる型に適用することで、
**型安全性を保ちつつ柔軟な拡張や再利用が可能**となります。

まずは単純な例で、ジェネリクスがどのように型を汎用化できるか見てみましょう：

```ts
// ジェネリクスを使った配列作成関数
function createList<T>(item1: T, item2: T): T[] {
    return [item1, item2];
}

// 文字列リストを作成
const stringList = createList("Silkė", "Cepelinai");

// 数値リストを作成
const numberList = createList(2024, 1224);

console.log(stringList); // ["Silkė", "Cepelinai"]
console.log(numberList); // [2024, 1224]

// 複合リストを作成
const list = createList("A", 42); // 以下のエラーになる：
// Argument of type 'number' is not assignable to parameter of type 'string'.(2345)
```

`createList<T>` の `<T>` がポイントです。
ジェネリック型（汎用型）`T` は、引数の型から自動的に推論され、
`string` や `number` など、あらゆる型でリストを作成できる、
ユーティリティ（共通）関数へと柔軟に変化します。
また、完全に異なる型を混ぜるとコンパイルエラーとなることから、
安全性が確保されています。

:::note info
ジェネリック型名には、慣習的に `T` などの大文字アルファベットを使用します[^ジェネリック型名]。
:::

[^ジェネリック型名]: TypeScript公式ドキュメントを見ると、最初のジェネリック型名が `Type` だったところから、後々 `T` などになっているので、おそらく `Type` の略称として `T` を使用していると考えられる：
  [TypeScript: Documentation - Generics](https://www.typescriptlang.org/docs/handbook/2/generics.html)

これにより、一度定義したこの `createList` 関数を、
文字列リスト・数値リスト・その他の型リストなど、
さまざまな場面で再利用できます。

まさに、基本の<ruby><rb>Silkė</rb><rt>シルケ</rt></ruby> <ruby><rb>su</rb><rt>ス</rt></ruby> <ruby><rb>svogūnais</rb><rt>スヴォグーナイス</rt></ruby>に様々な副菜・調味料を組み合わせることで
無数のレシピへと展開していく<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>の食卓のように、
同じ関数が多彩な「味わい」を生み出すのです。

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/b6fd2e40-630e-ffd9-02b0-8807967cc16c.png" alt="伝統的なSilkė su svogūnaisのレシピ" width="80%" /> |
| :-: |
| **伝統的な<ruby><rb>Silkė</rb><rt>シルケ</rt></ruby> <ruby><rb>su</rb><rt>ス</rt></ruby> <ruby><rb>svogūnais</rb><rt>スヴォグーナイス</rt></ruby>のレシピたち**[^tradicineSilkėSuSvogūnaisReceptų]<br />1000件以上のレシピが存在するほど、多彩な組み合わせが可能 |

[^tradicineSilkėSuSvogūnaisReceptų]: 画像はリトアニアの料理レシピサイトLa Maistasから拝借：
  [tradicine silke su svogunais | La Maistas](https://www.lamaistas.lt/paieska?q=tradicine%20silke%20su%20svogunais)
  ほかのレシピを検索すると多くても800とかなので、Silke su svogunaisのレシピは本当に豊富なことがわかる。

`createList()` のようなシンプルな関数は、
プロジェクト全体で共通的な機能（ユーティリティ）として再利用できます。
将来的に新たなデータ型が追加されても、この関数は同じロジックを維持したまま、
新たな「型の味」に対応できるのです。

次に、ジェネリクスを応用した例をいくつかご紹介します。



## 複数ジェネリクス：広がる組み合わせ

ジェネリクスは1つの型引数にとどまりません。
複数の型引数を組み合わせることで、より高度なデータ操作が可能となります。

たとえば、以下の `zip` 関数は2つの配列を組み合わせ、要素をペア化します：

```ts
function zip<T, U>(listA: T[], listB: U[]): Array<[T, U]> {
    const length = Math.min(listA.length, listB.length);
    const result: Array<[T, U]> = [];
    for (let i = 0; i < length; i++) {
        result.push([listA[i], listB[i]]);
    }
    return result;
}

// 文字列の料理名と数値の年号をペア化
const dishes = ["Silkė su svogūnais", "Cepelinai"];
const years = [2024, 1224];
const paired = zip(dishes, years);

console.log(paired);
// [ [ "Silkė su svogūnais", 2024 ], [ "Cepelinai", 1224 ] ]
```

ここでは `T` と `U` の2種類のジェネリック型を用いることで、
異なる型のデータを整然とまとめることができます。
これにより、ユーザーIDとユーザー情報、日付とイベント、キーと値など、
無数の組み合わせに対応可能な汎用ツールとなります。

これは、料理の多種多様な組み合わせを生み出す過程と似ています。
1つの「調理法（関数）」を、さまざまな「型（食材）」で組み合わせることで、
無数のバリエーションを実現できるのです。



## interfaceでの応用：更なる拡張性

ジェネリクスは、関数だけでなくクラスやインターフェース、型エイリアスなど、
TypeScriptで定義可能な、あらゆる型定義要素で利用可能です。

たとえば、以下のインターフェース `ApiResponse<T>` は、
データ部分 `data` の型を汎用的に扱えます：

```ts
interface ApiResponse<T> {
    status: number;
    data: T;
}

function fetchItem<T>(url: string): Promise<ApiResponse<T>> {
    // 実装は省略（HTTPリクエストを送って結果を返す想定）
    return fetch(url)
        .then(res => res.json())
        .then(json => ({ status: 200, data: json as T }));
}

interface Recipe {
    name: string;
    origin: string;
    // ... 他のプロパティ
}

fetchItem<Recipe>("/api/recipe")
    .then(response => {
        console.log(response.data.name); // TがRecipeとして扱える
    });
```

`ApiResponse<T>` は `T` の部分を他のデータ型に差し替えられます。
関数と同様、調理法（インターフェース定義）を他の食材（型）に適用できる構造です。

TypeScriptに標準で存在する `Array<T>` や `Promise<T>` などの型も
ジェネリクスによって汎用化されており[^TypeScript組み込み型]、これら組み込み型の存在は、
**ジェネリクスがTypeScript言語全体を支える基礎的な概念である**ことを示しています。

また、ReactやAngular、NestJSなどのフレームワーク、各種ライブラリも
ジェネリクスを駆使して汎用性の高いAPIを提供しています[^ライブラリでのジェネリクス使用]。

[^TypeScript組み込み型]: たとえばTypeScriptの `Array` なら `Array<T>` のようにジェネリクスを使っている：
  [TypeScript/src/lib/es5.d.ts at main · microsoft/TypeScript](https://github.com/microsoft/TypeScript/blob/main/src/lib/es5.d.ts#L1304)

[^ライブラリでのジェネリクス使用]: たとえばReactならGitHubの `packages/react/ReactHooks.js` を見ると、useStateにジェネリクスを使っていることがわかる：
  [react/packages/react/src/ReactHooks.js at main · facebook/react](https://github.com/facebook/react/blob/7283a213dbbc31029e65005276f12202558558fc/packages/react/src/ReactHooks.js#L96)

まさにエコシステム全体が、基本の「型料理法」を拡張しているのです。



## 型制約：質の保証

ジェネリクスは「自由さ」を生みますが、
時に「特定の条件を満たす型だけ許可したい」となることもあります。
そこで登場するのが `extends` などを用いた型制約です。

たとえば、グローバル展開を視野に入れたメニューや商品情報を考えてみましょう。
リトアニア語の「Silkė su svogūnais」に英語や日本語の翻訳を必ず用意する、
といったルールを型で強制する例を示します：

```ts
type RequiredLocales = "lt" | "en" | "ja";

function createLocalizedRecipe<
    T extends { name: string; origin: string },
    U extends Record<RequiredLocales, string>
>(baseItem: T, localizedNames: U): T & { localizedNames: U } {
    return { ...baseItem, localizedNames };
}

// リトアニア発のシルケ料理データ
const silkeBase = { name: "Silkė su svogūnais", origin: "Lithuania" };
// ローカライズ辞書には必ずlt, en, jaが必要
const silkeTranslations = {
    lt: "Silkė su svogūnais",
    en: "Herring with Onions",
    ja: "ニシンの玉ねぎ添え"
} as const;
// 問題なくオブジェクトが生成される
const localizedSilke = createLocalizedRecipe(silkeBase, silkeTranslations);

// 以下はエラー（jaが欠けているため）
const cepelinaiBase = { name: "Cepelinai", origin: "Lithuania" }
const cepelinaiTranslations = {
    lt: "Didžkukuliai",
    en: "Cepelinai"
};
createLocalizedRecipe(cepelinaiBase, cepelinaiTranslations);
// Argument of type '{ lt: string; en: string; }' is not assignable to parameter of type 'Record<RequiredLocales, string>'.
// Property 'ja' is missing in type '{ lt: string; en: string; }' but required in type 'Record<RequiredLocales, string>'.
```

ここでは、`T` は `name`・`origin` を必ず持ち、
`U` は `RequiredLocales` 内のすべての言語キーを持つ必要があります。
つまり、欠けた言語があればコンパイルエラーが発生するため、
こうした制約のあるジェネリクスは、品質を型レベルで自動的に保証できます。
将来的に言語が増えても、型制約を変更するだけで、一貫して品質を保証し続けられます。

:::note info
型制約はこれだけにとどまらず、条件型 （ `T extends ... ? ... : ...` ）や
後述するMapped Types、Utility Typesと組み合わせることで、
さらに高度な抽象化やエラーチェックが可能です。
こうした仕組みは、規模拡大や要件変化に対応するうえで強力な武器となります。
:::

<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>において肉や乳製品を避けるルールを守りつつ多様な料理が展開されるように、
型制約はジェネリクスが紡ぐ「型の食卓」に一定の秩序と品質基準を与えるのです。










# 05. keyof：Koldūnai

<ruby><rb>Koldūnai</rb><rt>コルドゥーナイ</rt></ruby>はリトアニアで親しまれるダンプリングの一種で[^ダンプリング]、
中に肉やチーズ、キノコなど様々な具が包まれた小さな包み料理です[^Koldūnai]。

[^ダンプリング]: ダンプリングとは、餃子などの、練った小麦粉などを茹でたり焼いたりしたもののこと：
  [ダンプリング - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%80%E3%83%B3%E3%83%97%E3%83%AA%E3%83%B3%E3%82%B0)

[^Koldūnai]: Koldūnaiはリトアニア料理というわけでもない：
  [カルドゥヌイ - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AB%E3%83%89%E3%82%A5%E3%83%8C%E3%82%A4)

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/a2fbca7f-7003-0825-0e21-9e2515f0fe9a.png" alt="Koldūnai su grybais（キノコ入り餃子）" width="80%" /> |
| :-: |
| **<ruby><rb>Koldūnai</rb><rt>コルドゥーナイ</rt></ruby> <ruby><rb>su</rb><rt>ス</rt></ruby> <ruby><rb>grybais</rb><rt>グリーバイス</rt></ruby>（キノコ入り餃子）[^Koldūnaiの画像]** |

[^Koldūnaiの画像]: 画像はリトアニアの園芸・農業サイトDerlingas.ltから拝借：
  [Koldūnai su grybais - tradicinis Kūčių patiekalas](https://derlingas.lt/grybu-patiekalai/koldunai-su-grybais-kucioms-tradicinis-patiekalas-valgomas-tik-karta-metuose)

`keyof` 型演算子は、オブジェクトという皮の中に何が詰まっているかを
型レベルで、キー一覧としてユニオン型で取り出すものです。

たとえば以下を見てみましょう：

```ts
type Dish = {
  name: string;
  ingredients: string[];
  origin: string;
};

type DishKeys = keyof Dish; // "name" | "ingredients" | "origin"
```

<ruby><rb>Koldūnai</rb><rt>コルドゥーナイ</rt></ruby>が「肉」「チーズ」「キノコ」などいくつもの具材オプションを持つように、
`keyof Dish` は `Dish` 型が持つキー名群リテラルのユニオン型を返します。



## プロパティへのアクセス

以下のように、関数の引数に `keyof` を使うことで、
オブジェクトのプロパティを安全に指定できます：

```ts
function getProperty(obj: Dish, key: keyof Dish) {
  return obj[key]; // keyがTに確実に存在することが型で保証される
}

const dish: Dish = {
  name: "Koldūnai",
  ingredients: ["flour", "water", "mushrooms"],
  origin: "Lithuania"
};

// OK
const dishName = getProperty(dish, "name"); 
// "name"キーが確実に存在するため、安全

// NG
const dishColor = getProperty(dish, "color"); // コンパイルエラー：
// Argument of type '"color"' is not assignable to parameter of type 'keyof Dish'.
```

`keyof` は、オブジェクト型を操作するTypeScriptにおける基礎的な機能です。
<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>が12皿で成り立つように、`keyof` も他の機能と組み合わせるものです。
以降の章でも `keyof` を使用しているので、その用法に注目してみてください。










# 06. typeof：Kibinai

<ruby><rb>Kibinai</rb><rt>キビナイ</rt></ruby>は、<ruby><rb>Koldūnai</rb><rt>コルドゥーナイ</rt></ruby>のように具を包むものですが、ダンプリングというよりはパイです[^パイ]。
<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>に登場することは珍しいのですが、おいしいのでここで紹介してしまいます[^Kibinaiが美味しい理由]。

[^パイ]: さらにいうと、生地がバターを練り込むショートクラストペイストリーに近いので、パイというよりはペイストリーというほうが正しそうだが、これ以上は料理の専門家に任せることとする。

[^Kibinaiが美味しい理由]: リトアニアに来ると、まずCepelinaiなど芋料理を多く食べることになるため、次の日から食べる「!(芋)料理」が非常に美味しく感じる。

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/a9ca2929-c98d-f887-45ed-fc11d4ef1522.png" alt="Kibinai（2018／トラカイ城周辺のレストラン）" width="80%" /> |
| :-: |
| **Kibinai（2018／トラカイ城周辺のレストラン）**<br />Kibinaiは美味しかったし、トラカイ城は綺麗だったし、最高！ |

`typeof` 型演算子は、見た目は `keyof` 型演算子と似ていますが、機能は全く異なります。
これは、値から型情報を抽出するものです。

:::note info
JavaScriptにも同名演算子があります。
結果は文字列としてのみ返され、以下のように基本的な型しか返しません：

<details>
<summary>JavaScriptにおける各種値の <code>typeof</code> 実行結果</summary>

```js
console.log(typeof 1991);           // "number"
console.log(typeof "Lietuva");      // "string"
console.log(typeof true);           // "boolean"
console.log(typeof undefined);      // "undefined"
console.log(typeof null);           // "object" （注意! 言語仕様のバグ）
console.log(typeof {});             // "object"
console.log(typeof []);             // "object" （配列もobjectと判定される）
console.log(typeof function () {}); // "function"
```
</details>
:::

値が型より先にあるところが、これまで紹介した機能と異なります。
料理名の変数から、その型がなにか取得して確認しましょう：

```ts
const dishName = "Pyragėliai";

// 値dishNameから型を取り出す
type DishNameType = typeof dishName; 
// type DishNameType = "Pyragėliai"
```

ここでは文字列定数からリテラル型を抽出できています。
このように値から型をつくれるようになると、たとえば以下の場面で応用できます：

- 定数の型化
- APIレスポンスにおけるモックデータからの型生成
- 型の動的生成によるコード簡略化
- モジュール間の依存解決
- 型ガード

以下の応用例を見て、その可能性の一端を見てみましょう。



## 配列からユニオン型の作成

以下のように、1次元配列を、constアサーションを使いWideningを解いた状態で
`typepf` と `[number]` ルップアップを利用すると、要素をユニオン型にできます：

```ts
// 配列の定義
const dishes = ["Kibinai", "Koldūnai", "Kūčiukai"] as const;
// dishes: readonly ["Kibinai", "Koldūnai", "Kūčiukai"]
// as constがないと stirng[] になる点に注意

// 型の取得
type Dish = (typeof dishes)[number];
// Dish = "Kibinai" | "Koldūnai" | "Kūčiukai"

// ルックアップの補足
type Dish0 = (typeof dishes)[0];
// Dish0 = "Kibinai"
type Dish12 = (typeof dishes)[1 | 2];
// Dish12 = "Koldūnai" | "Kūčiukai"
```

この用法は、以下のような場面で便利です：

- スタイルやテーマの管理
- APIレスポンスの配列を型として扱う
- フォームのバリデーション
- データベースのカラム



## filterによる型フィルター

これまでは`typeof` の結果を型として受け取っていましたが、
以下の通り、JavaScriptのように文字列として受け取ることも可能です：

```ts
const array = ["Kibinai", 1991, "Koldūnai", 1253, "Kūčiukai"];
// array: array: (string | number)[]
const filtered = array.filter((val) => typeof val === "string");
// filtered: string[] = ["Kibinai", "Koldūnai", "Kūčiukai"]
```

ここでは `string` と `number` の両方が要素に含まれている配列に対して
`.filter()` で要素を走査して、`typeof val === "string"` で文字列のみを抽出することで、
フィルター後配列が文字列配列 `string[]` になることを確認できています。

:::note info
値が型を満たすか判別できるものとして、`instanceof` 演算子もあります。
`typeof` では値がプリミティブ型（`string` など）かを判別する際に使用しますが、
`instanceof` ではオブジェクトがクラスのインスタンスかを判定する際に使用します[^instanceofの用法]。
:::

[^instanceofの用法]: TypeScriptにおける `instanceof` の活用については、Narrowingについての公式ドキュメントが参考になる：
  [TypeScript: Documentation - Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#instanceof-narrowing)

この用法は、以下のような場面で便利です：

- データの正規化
- フロントエンドのDOM操作
- APIレスポンスの型フィルタリング
- ユニオン型の絞り込み



## 型ガードへの応用

未知の料理を食べるときは不安になるように、外部から取得したデータは信用なりません。
その値が期待していた型を満たすか実行するまでわからないことがあります。
そこで用いられるのが、`typeof` などによる型ガード（Type Guard）です：

```ts
const input: unknown = "Kibinai";

console.log(input.toUpperCase()) // コンパイルエラー：
// 'input' is of type 'unknown'.

// typeof型ガードの使用
if (typeof input === "string") {
  console.log(input.toUpperCase()); // KIBINAI
}

// ユーザー定義型ガードの使用
function isString(value: unknown): value is string {
  return typeof value === "string";
}
if (isString(input)) {
  console.log(input.toUpperCase()); // KIBINAI
}
```

型ガードがない状態では `input` が `unknown` 型であるために、`string` の扱いにはならず
`toUpperCase()` メソッドを使用しようとすると、コンパイルエラーになります。
そこで、`typeof` により型を確認し、その型が `"string"` であることを確認し、
`if` でその条件下における実行であることを明示することで、
TypeScriptは `if` 下では `input` が `string` であることを判断できるため、
`toUpperCase()` メソッドを使用しても、エラーになりません。

また、その下部では、`is` を使った、型述語（Type Predicates）による
「ユーザー定義型ガード（User-Defined Type Guard）」という手法も使っています[^ユーザー定義型ガード]。
ここでは値の型を確認していますが、主にオブジェクトや配列の、構造の確認に用います。
`typeof` を使用した型ガードと同様に、エラーなくメソッドを実行できています。

[^ユーザー定義型ガード]: Type PredicatesもUser-Defined Type Guardも公式ドキュメントで使用されている言葉である：
  [TypeScript: Documentation - Narrowing（旧ドキュメント）](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
  [TypeScript: Documentation - Advanced Types（新ドキュメント）](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)

:::note warn
この `is` によるユーザー定義型ガードは、
型注釈を用いて強引に型推論を変えてしまうものです。

たとえば、以下の通り、明らかに実装では `number` か確認している関数でも、
型注釈に `[引数] is string` を加えてもコンパイルエラーにはならず、
TypeScriptは関数が `string` か確認したものと認識します：

```ts
const array = ["Kibinai", 1991, "Koldūnai", 1253, "Kūčiukai"];

function isString(value: unknown): value is string {
  return typeof value === "number"; // 明らかに型注釈と矛盾した実装
}

array.forEach((val) => {
  if (isString(val)) {
    console.log(val, typeof val, "isString", val.toUpperCase());
  } else {
    console.log(val, typeof val, "isNumber", val*2);
  }
})

// output:
// [LOG]: "Kibinai",  "string",  "isNumber",  NaN 
// [ERR]: "Executed JavaScript Failed:" 
// [ERR]: val.toUpperCase is not a function 
```

実行すると当然、上記のエラーになります。
そのため、`is` を用いたユーザー定義型ガードを実装する際には、
実装と型定義に矛盾が無いことを必ず確認すべきでしょう。
:::









# 07. Mapped Types：Silkė su grybais

Mapped Typesは `{ [P in K]: ... }` の構文を使い、
ユニオンを元に新たな型を生成する機能です[^MappedTypesとは]。

[^MappedTypesとは]: 公式ドキュメントの表現を参考にしています：
  [TypeScript: Documentation - Mapped Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html)

Genericsと同様に強力な機能であるために、非常に多様な場面で使えます。
応用的なコード例を見ることで、その一端に触れてみましょう：

```ts
type Writable<T> = { -readonly [K in keyof T]: T[K] };
```

ここでは、`keyof T` でGeneric型 `T` のキーを取得し、それを `K` として走査しています。
`-readonly` はプロパティから `readonly` 修飾子を取り除く記号です。
つまり、読み取り専用だったオブジェクト型を、書き込み可能な型へと変換します。

:::note warn
Mapped Typesでは、以下の通り追加のプロパティを定義できません：

```ts
type Dish = {
  [K in string]: string;
  name: string; // コンパイルエラー：
  // A mapped type may not declare properties or methods.
};
```
:::

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/4771d040-371d-3f49-a95f-4ed75a655926.png" alt="Silkė su grybais ir pupelėmis（ニシンのキノコと豆添え）" width="80%" /> |
| :-: |
| **<ruby><rb>Silkė</rb><rt>シルケ</rt></ruby> <ruby><rb>su</rb><rt>ス</rt></ruby> <ruby><rb>grybais</rb><rt>グリーバイス</rt></ruby> <ruby><rb>ir</rb><rt>イル</rt></ruby> <ruby><rb>pupelėmis</rb><rt>プペレミス</rt></ruby>（ニシンのキノコと豆添え）**[^SilkėSuGrybais] |

[^SilkėSuGrybais]: 画像はLa Maistasから拝借：
  [Silkė su grybais ir pupelėmis - receptas | La Maistas](https://www.lamaistas.lt/receptas/silke-su-grybais-ir-pupelemis-65417)

<ruby><rb>Silkė</rb><rt>シルケ</rt></ruby> <ruby><rb>su</rb><rt>ス</rt></ruby> <ruby><rb>grybais</rb><rt>グリーバイス</rt></ruby>（ニシンのキノコ添え）では
ベースのニシン料理にキノコを加え、風味や食感を「再構成」しているように、
Mapped Typesは元の型をもとにプロパティを自在に組み直し、
新たな型を生み出す強力な仕組みなのです。












# 08. Utility Types：Silkė su burokėliai

TypeScriptには、GenericsやMapped Typesを用いて実装された
便利なUtility Types（ユーティリティ型）が数多く用意されています。

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/affee0f1-3b34-0940-c0a7-6941a5d10434.png" alt="Silkė burokėlių patale（ニシンのビーツ乗せ）" width="100%" /> |
| :-: |
| **<ruby><rb>Silkė</rb><rt>シルケ</rt></ruby> <ruby><rb>burokėlių</rb><rt>ブロケーリウ</rt></ruby> <ruby><rb>patale</rb><rt>パタレ</rt></ruby>（ニシンのビーツ乗せ）**[^GreitaSilkėBurokėliųPatale] |

[^GreitaSilkėBurokėliųPatale]: 画像はLa Maistasから拝借：
  [Greita silkė burokėlių patale - receptas | La Maistas](https://www.lamaistas.lt/receptas/silke-burokeliu-patale-11771)

:::note info
コードは `lib.es5.d.ts` を参照しています[^lib.es5.d.ts]。
:::

[^lib.es5.d.ts]: GitHubから確認できます：
  [TypeScript/src/lib/es5.d.ts at main · microsoft/TypeScript](https://github.com/microsoft/TypeScript/blob/main/src/lib/es5.d.ts)

Genericsで柔軟に型を適用し、Mapped Typesで動的に型構造を再構成し、
Utility Typesで多様なシナリオを手軽に表現する――
こうした仕組みは、リトアニア料理が伝統的な素材を基盤に、
玉ねぎ、キノコ、ビーツといったさまざまな要素を組み合わせて、
多彩な風味や食感を生み出す過程と似ています。



## PartialとRequired：添えるべきかどうか

```ts
/**
 * Make all properties in T optional
 */
type Partial<T> = {
    [P in keyof T]?: T[P];
};
```

`Partial<T>` は `T` のすべてのプロパティをオプショナル(`?`)にします[^オプショナル]。
このように、Mapped Typesを使うことで、
将来の拡張や不完全なデータへの対応が容易になります。

[^オプショナル]: オプショナルとは、通常は設定されている必要のあるプロパティを、プロパティを設定するかは任意にすることができる機能である：
  [TypeScript: Documentation - Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties)

逆に、すべてのプロパティを必須にする `Required` も存在します：

```ts
/**
 * Make all properties in T required
 */
type Required<T> = {
    [P in keyof T]-?: T[P];
};
```

これで、`T` の全プロパティが必須となります。
「必ずビーツを添える」ルールを型レベルで強制できるようなものです。



## Readonly：食べちゃダメ！

```ts
/**
 * Make all properties in T readonly
 */
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
```

`Readonly<T>` は `T` のすべてのプロパティを読み取り専用に変換します。
Mapped Typeで定義されており、副作用のない安全なデータ構造を簡単に表現できます[^副作用のない安全なデータ構造]。

[^副作用のない安全なデータ構造]: 副作用のない安全なデータ構造とは、オブジェクトのプロパティを書き換えないことで、予期せぬバグや動作を防ぎ、データフローを明確化する手法のことを指している。



## RecordとPick：思い通りの組み合わせ

```ts
/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends keyof any, T> = {
    [P in K]: T;
};
```

`Record<K, T>` は指定したキーと値型で、新たな「材料の対応表」を作ることができます。  
たとえば、`Record<string, number>` は「食材名：必要量」のような辞書型を表現できます。  
リトアニアの食卓で、各家庭が独自のスパイスやハーブ、野菜を組み合わせて、
それぞれの「味の辞書」を紡ぎ出すイメージです。

```ts
/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
```

一方、`Pick<T, K>` は既存の型 `T` から必要な「要素」だけを選び出すことで、
たとえば豊富な副菜から特定の食材を抽出し、新たな一皿として提供するようなもの。
ビーツや玉ねぎ、キノコなどの素材から好きなものをピックアップする感覚です。



## Omit：引き算の重要性

```ts
/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
```

`Omit<T, K>` は逆に、`T` から指定したプロパティ `K` を除外します。
これは、特定の要素を「入れない」ことで、味や風味を調整する行為に似ています。
たとえば、玉ねぎが苦手な人向けに玉ねぎを抜いた<ruby><rb>Silkė</rb><rt>シルケ</rt></ruby>料理を提供するように、
`Omit` は不要な要素を型レベルで取り除くことで、要件に合わせて「引き算」します。










# 09. Index Signature：Mišrainė

GenericsやMapped Types、Utility Typesは、  
オブジェクトのプロパティを柔軟に扱う手段を提供しました。  
しかし、キー名が事前にわからないオブジェクトもあります。

TypeScriptは、Index Signature（インデックス型）でこの問題に対処します[^IndexSignature]。
たとえば `[key: string]: T` という書き方で、
「キー名は自由だが、値の型は `T`」というルールを表します。

[^IndexSignature]: Index Signatureについての表現は公式ドキュメントを参照している：
  [TypeScript: Documentation - Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures)

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/af7c7c8e-b6f4-dca4-8ab5-219501a35c53.png" alt="ビーツや豆、コハダなどを用いたMišrainė（筆者宅／2021）" width="80%" /> |
| :-: |
| **ビーツや豆、コハダなどを用いた<ruby><rb>Mišrainė</rb><rt>ミシュライネ</rt></ruby>（筆者宅／2021）**<br />今年の社内忘年会では、じゃがいもを使ったものを提供しました |

<ruby><rb>Mišrainė</rb><rt>ミシュライネ</rt></ruby>に決まった材料はありません。
野菜、豆、穀物、時には卵や魚まで、多様な食材が入りえます。
共通するのは、すべてが細かく刻まれ、混ざり合って一皿を構成することです。

以下は、Index Signatureで<ruby><rb>Mišrainė</rb><rt>ミシュライネ</rt></ruby>を表した例です。  
このサラダは、どんな食材名でも受け入れますが、値は常に同じ型構造を保ちます：

```ts
type IngredientCategory = 'vegetable' | 'grain' | 'bean' | 'other';
type IngredientTexture  = 'crunchy' | 'soft' | 'firm';
interface IngredientInfo {
    category: IngredientCategory;
    texture: IngredientTexture;
};

interface Mišrainė {
    [ingredient: string]: IngredientInfo;
}

const salad: Mišrainė = {
    beet: { category: 'vegetable', texture: 'soft' },
    bean: { category: 'bean', texture: 'firm' }
};

// 後から別の材料も追加可能
salad.cucumber = { category: 'vegetable', texture: 'crunchy' };
salad.bread = { category: 'grain', texture: 'firm' };
```

キー名（食材名）は自由、でも値の型 `IngredientInfo` は一定。
こうして、未知のキーを扱う柔軟性と、一定の型ルールを両立できます。

:::note info
Index Signatureでは、Mapped Typesとは異なり、
以下の通り、追加のプロパティを定義することができます：

```ts
interface Dish {
    [ingredient: string]: string;
    name: string;
}
```
:::

Index Signatureは、GenericsやMapped Typesとも異なる方向での柔軟性をもたらします。
多様な材料を受け入れつつ一貫した型構造を維持する、まさに<ruby><rb>Mišrainė</rb><rt>ミシュライネ</rt></ruby>のような存在です。



## 他の形による表現

Index Signatureの書き方として `[key: string]: T` を紹介しましたが、
Utility Typesである `Record<K, T>` やMapped Typesでも同様に表現できます。
たとえば、以下の3つの型は、同じ意味になります：

```ts
type IndexSignature = { [x: string]: number };
type Recorded       = Record<string, number>;
type MappedTypes = { [x in string]: number };
// いずれも { [x: string]: number; } と推論される
```

実際に、推論された型を見ると、いずれも `{ [x: string]: number; }` の形になっています。
ただ、読みやすさと慣習や、以下のようなパラメータに対する制約があることから、
シンプルに実装するために、`[key: string]: T` の記法が用いられます：

```ts
type Literal = ""
type IndexSignatureError = { [x: Literal]: number }; // コンパイルエラー：
// An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.
type RecordedLieral      = Record<Literal, number>;
type MappedTypesLiteral  = { [x in Literal]: number };
```



## 再帰によるネストへの対応

Index Signatureの値に自身を含むことで、ネストできるようになります。
この再帰的な構造は、複雑な階層的データを自然に表現できるようになり、
フロントエンドではCSSセレクタを構造で示せるようになります[^再帰によるネストへの対応]。

[^再帰によるネストへの対応]: TypeScript Deep Driveによれば、JSライブラリにおけるCSSの共通パターンでも、この再帰が使われている：
  [Index signature（インデックス型） | TypeScript Deep Dive 日本語版](https://typescript-jp.gitbook.io/deep-dive/type-system/index-signatures#dezainpatnnesutosaretaindekkusushigunecha)

以下は、<ruby><rb>Mišrainė</rb><rt>ミシュライネ</rt></ruby>の複雑な構造を、ネストしたIndex Signatureで示したものです：

```ts
interface Mišrainė {
  base?: string;
  [ingredient: string]: string | Mišrainė | undefined;
}

const mišrainė: Mišrainė = {
  base: 'じゃがいも',
  'ドレッシング': {
    base: 'マヨネーズ',
    '胡椒': '黒胡椒',
    'ハーブ': 'バジルとオレガノ'
  },
};
```



## neverでプロパティ除外

`never` を使用することで、指定したプロパティを設けなくさせることができます。
以下では、私の嫌いなトマトを<ruby><rb>Mišrainė</rb><rt>ミシュライネ</rt></ruby>に絶対に含めないようにしています：

```ts
type Mišrainė = {
  // トマトプロパティを除外するために `tomato` を `never` に設定
  tomato?: never;
  [key: string]: any;
};

const mišrainė: Mišrainė = {
  lettuce: "fresh",
  cucumber: "crisp",
  tomato: "ripe", // コンパイルエラー：
  // Type 'string' is not assignable to type 'undefined'.
  // The expected type comes from property 'tomato' which is declared here on type 'Mišrainė'
};
```




## 再帰と除外の、更なる応用

再帰と除外を応用することで、以下のような複雑な構造をつくることができます：

```ts
// リーフノード: quantityのみを持つ
interface LeafIngredient {
  quantity: string;
};

// ネストノード: 他の具材を持ち、quantityを持たない
type NestedIngredient = {
  [ingredient: string]: Ingredient;
} & { [key in keyof LeafIngredient]? : never };

// 再帰的な型
type Ingredient = LeafIngredient | NestedIngredient;

// トップレベルではquantityを禁止
type TopLevelIngredients = {
  [K in string as K extends keyof LeafIngredient ? never : K]: Ingredient;
};

// 正しいオブジェクト例
const complexRecipe: TopLevelIngredients = {
  vegetables: {
    quantity: "500g"
  },
  marinade: {
    herbs: { quantity: "10g" },
    spices: { quantity: "5g" }
  },
  dressing: {
    base: { quantity: "100ml" },
    extras: {
      lemon: { quantity: "2 slices" },
      honey: { quantity: "1 tbsp" }
    }
  }
};

// エラーとなるオブジェクト例
const ng1: TopLevelIngredients = { 
  quantity: "1g" // コンパイルエラー（ルートにquantityは書けない）：
  // Type 'string' is not assignable to type 'Ingredient'.
  // The expected type comes from property 'quantity' which is declared here on type 'TopLevelIngredients'
}

const ng2: TopLevelIngredients = { 
  a: { 
    quantity: "1g", 
    b: {} // コンパイルエラー（'quantity' と他のプロパティを同時に持つことはできない）：
    // Object literal may only specify known properties, and 'b' does not exist in type 'LeafIngredient'.
    // The expected type comes from property 'a' which is declared here on type 'TopLevelIngredients'
  } 
}

const ng3: TopLevelIngredients = { 
  a: { 
    quantity: {a: ""}, // コンパイルエラー（quantityはstringしか書けない）：
    // Type '{ a: string; }' is not assignable to type 'string'.
    // The expected type comes from property 'quantity' which is declared here on type 'Ingredient'
  }
}
```

この `TopLevelIngredients` の構造には、以下のルールがあります：

- 値の型は以下のいずれかである
    - リーフノード（ここでは `LeafIngredient = { quantity: string }`
    - ネストノード（リーフノードかネストノードの、どちらかを値に持つ）
- トップレベルにはリーフノードを含まない
- ネストノードのキー名には、リーフノードのキーを含まない
- 再帰的にネストを許容する

このような構造は、アプリケーションの設定やテーマ設定など、階層的なオプションを持つ設定ファイルで、特にスタイルシートのテーマ設定において、各セクションにのみ特定のプロパティ（例えば、色やフォントサイズ）を許可する場合で有用です。










# 10. Conditional Types：Šaltibarščiai

リトアニア料理の代表格の1つが、ピンク色の冷製ビーツスープ<ruby><rb>Šaltibarščiai</rb><rt>シャルティバルシチ</rt></ruby>です。
「冷たいボルシチ」と呼ばれるこの料理は、ビーツ・ケフィア・ディルなどが混ざり合い、
トッピングやビーツの量などの条件によって味のバリエーションが生まれます。
また、ポーランドではChłodnik litewskiと呼ばれ、土地柄でも風味に変化があります。

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/6b2ddb66-7b26-98bf-292b-0850f447c8c5.png" alt="Šaltibarščiai" width="80%" /> |
| :-: |
| **<ruby><rb>Šaltibarščiai</rb><rt>シャルティバルシチェイ</rt></ruby>**[^Šaltibarščiaiの画像]<br/> |

[^Šaltibarščiaiの画像]: La Maistasより拝借：
  [Šaltibarščiai - receptas | La Maistas](https://www.lamaistas.lt/receptas/saltibarsciai-11127)

Conditional Types（条件型）は以下の通り、条件次第で型を選び分けるものです[^ConditionalTypes]：

[^ConditionalTypes]: Conditional Typesの詳細は
[TypeScript: Documentation - Conditional Types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html)

```ts
type IsDish<T> = T extends Dish ? true : false;
```

`extends` を用いて、三項演算子のように `?` と `:` を使い、条件分岐しています。
ここでは、Generic型 `T` が `Dish` に割り当て可能かで分岐しており、
可能な場合は `true` リテラル型を、そうでない場合は `false` リテラル型を返しています。

<ruby><rb>Šaltibarščiai</rb><rt>シャルティバルシチ</rt></ruby>が、ビーツを増やせばより鮮やかで甘みある味に、
ディルを控えればさっぱりとした飲み口に、と条件次第で味わいが変化するように
`T` の「状態・条件」に応じて型が変わるのです。




## ユーティリティ型の作成

Conditional Typesは高度なユーティリティ型を実装するのにも向いています。
たとえば `never` と組み合わせて、以下のように特定のプロパティを除外できます：

```ts
type Without<T, U> = T extends U ? never : T;

type Ingredients = "carrots" | "potatoes" | "tomato";

type MyIngredients = Without<Ingredients, "tomato">;
// MyIngredients = "carrots" | "potatoes"
```

Conditional Typesで `never` を返すことで、その型をユニオン型から削除できます。
それにより、私の苦手なトマトを<ruby><rb>Šaltibarščiai</rb><rt>シャルティバルシチ</rt></ruby>の具材から除くことに成功しました。



## Union Distribution

Conditional Typesでは、型パラメータがユニオン型である場合に
Union distribution（ユニオンの分配）が発生します。
先ほど例に出した、料理かどうかチェックする `IsDish<T>` でもそれを確認できます：

```ts
type IsDish<T> = T extends Dish ? true : false;

type A = IsDish<Dish>;          // true
type B = IsDish<string>;        // false
type C = IsDish<Dish | string>; // boolean
```

`IsDish<T>` は、与えられた型 `T` が `Dish` に
割り当て可能なら `true` リテラルを、
そうでないなら `false` リテラルを返すものでした。

実際に、割り当て可能な `A` とそうでない `B` はその通りに返されています。
しかし、`Dish | string` をあてた場合には、`boolean` を返されています。
これは、`Dish` と `string` をそれぞれ確認した結果、`true | false` となり、
それが `boolean` の定義であるため、この結果となりました。

シェフが<ruby>Šaltibarščiai<rt>シャルティバルシチェイ</rt></ruby>の各具材を個別に刻んだり混ぜたりするように、
TypeScriptはConditional Typesにおいてユニオン型の要素を個別に評価していくのです。



## 深い型の再帰的な走査

<ruby><rb>Šaltibarščiai</rb><rt>シャルティバルシチェイ</rt></ruby>が多様な条件で複雑なバリエーションを産むように、
Conditional Typesも以下のように再帰することでネストの深い複雑な型に対応できます：

```ts
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// 使用例
interface Dish {
  name: string;
  ingredients: {
    vegetables: string[];
    meats: string[];
  };
}

type PartialDish = DeepPartial<Dish>;
/*
{
  name?: string;
  ingredients?: {
    vegetables?: string[];
    meats?: string[];
  };
}
*/
```










# 11. infer：Juoda Ruginė Duona

業務スーパーに行ったら、なんと黒いライ麦パンが売っていました。
リトアニアでは<ruby><rb>Juoda</rb><rt>ユオダ</rt></ruby> <ruby><rb>Ruginė</rb><rt>ルギネ</rt></ruby> <ruby><rb>Duona</rb><rt>ドゥオナ</rt></ruby>（黒いライ麦パン）と言い、
パンの日やパン祭りがあるほどに親しまれています[^パンの日]。

[^パンの日]: 2月5日がパンの日で、民間伝承でもパンが重要な立ち位置にあるそうです：
  [Juoda lietuviška duona išlieka mylimiausia – patarimai, padėsiantys ja mėgautis ilgiau](https://ve.lt/gyvenimas/laisvalaikis/juoda-lietuviska-duona-islieka-mylimiausia-patarimai-padesiantys-ja-megautis)
  パンを逆さまに置くと、家が火事になるとか……：
  [Tauragės krašto muziejus » Vasario 5-ąją minima Šv. Agotos, Duonos diena](https://tauragesmuziejus.lt/naujienos/vasario-5-aja-minima-sv-agotos-duonos-diena/)

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/44409f89-83e6-1643-80fa-0171a7273764.png" alt="業務スーパーのライブレッド" width="100%" /> |
| :-: |
| **業務スーパーのライブレッド**[^ライブレッド]<br />冷凍で売っていました |

[^ライブレッド]: 画像は業務スーパーから拝借：
[ライブレッド - 商品紹介｜プロの品質とプロの価格の業務スーパー](https://www.gyomusuper.jp/product/detail.php?go_id=6875)

`infer` 演算子は、Conditional Typesの中で型を推論（Infer）し、一部を抽出するものです。
これにより、複雑な型から特定の部分を取り出して再利用することができます。
例えば、関数の戻り値の型を抽出する例を考えてみます：

```ts
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

const bake = () => "黒パンは美味しい";

type Result = MyReturnType<typeof bake>; // Resultはstring型
```

ここでは、`infer` を使用しているUtility Types `ReturnType` を新たに定義し、
関数 `bake()` の戻り値の型を抽出し、`string` 型であることを確認しています。
Conditional Types（<ruby>Šaltibarščiai<rt>シャルティバルシチェイ</rt></ruby>）に `infer`（<ruby><rb>Juoda</rb><rt>ユオダ</rt></ruby> <ruby><rb>Ruginė</rb><rt>ルギネ</rt></ruby> <ruby><rb>Duona</rb><rt>ドゥオナ</rt></ruby>）を含むことで、
型システムの味わいが深まり、複雑な型の相互作用がスムーズに調和します。

なお、実際の `ReturnType` は次のように実装されています：

```ts
/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
```



## 配列の要素の型を抽出

`infer` を使って配列の要素の型を抽出することもできます。
黒パンの材料リストから材料の型を取り出す場合は、以下のようになります：

```ts
type ElementType<T> = T extends (infer E)[] ? E : never;

type DuonaIngredients = string[];

type Ingredient = ElementType<DuonaIngredients>; // string
```



## Utility Types「Awaited」の紹介

TypeScript 4.5で新たなUtility Types `Awaited` が追加されました[^4.5]。
`Promise<T>` の解決値の型を簡単に抽出できるもので、
これにより、複雑な非同期処理の型管理がより直感的かつ効率的になります。

[^4.5]: 下記の公式ドキュメントに記載がある：
  [TypeScript: Documentation - TypeScript 4.5](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#the-awaited-type-and-promise-improvements)

`Awaited` のソースは以下です[^Awaited]：

[^Awaited]: ソースは以下を参照：
[TypeScript/src/lib/es5.d.ts at main · microsoft/TypeScript · GitHub](https://github.com/microsoft/TypeScript/blob/main/src/lib/es5.d.ts#L1545)

```ts
/**
 * Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`.
 */
type Awaited<T> = T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
    T extends object & { then(onfulfilled: infer F, ...args: infer _): any; } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
        F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument
            Awaited<V> : // recursively unwrap the value
        never : // the argument to `then` was not callable
    T; // non-object or non-thenable
```

実装は複雑に見えますが、使用方法は以下の通り簡単で、ネストにも対応できます：

```ts
type NestedPromiseType = Promise<Promise<number>>;
type ResolvedNestedType = Awaited<NestedPromiseType>; // number
```

ここでは、`Promise<number>` から `Awaited` を使用して `number` 型を抽出しています。

非同期関数の戻り値の型を取得する際にも `Awaited` を活用できます。
以下の例では、非同期関数 `fetchDuona` の戻り値から解決値の型を抽出しています：

```ts
async function fetchDuona(): Promise<{ name: string; ingredients: string[] }> {
  return {
    name: "黒ライ麦パン",
    ingredients: ["ライ麦粉", "水", "塩", "酵母"],
  };
}

type FetchDuonaReturnType = Awaited<ReturnType<typeof fetchDuona>>;
// { name: string; ingredients: string[] }
```



## ReactコンポーネントのProps型抽出

Reactを使用している場合、とある家庭の黒パンの材料を調べるように、
以下の通り、コンポーネントのProps型を抽出することもできます：

```tsx
import React from 'react';

type ExtractProps<T> = T extends React.ComponentType<infer P> ? P : never;

type DuonaProps = {
  name: string;
  ingredients: string[];
};

const DuonaComponent: React.FC<DuonaProps> = ({ name, ingredients }) => (
  <div>
    <h1>{name}</h1>
    <ul>
      {ingredients.map((ingredient) => (
        <li key={ingredient}>{ingredient}</li>
      ))}
    </ul>
  </div>
);

type Props = ExtractProps<typeof DuonaComponent>; // DuonaPropsと同じ
```










# 12. Type Assertion：Cepelinai

型アサーションの説明には、リトアニアの国民食「<ruby><rb>Cepelinai</rb><rt>ツェペリナイ</rt></ruby>」が最適です[^ツェペリナイ]。
<ruby><rb>Cepelinai</rb><rt>ツェペリナイ</rt></ruby>はすりおろしたじゃがいもを「成形」して、中に肉を詰めた料理です。
その形状は飛行船「ツェッペリン」に似ており、名前の由来にもなっています。

[^ツェペリナイ]: 私が好きなリトアニア料理。芋料理なので食べすぎに注意。

| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534741/219624a8-d259-4fef-9fbf-c92f9e72d3dd.png" alt="Cepelinai" width="80%" /> |
| :-: |
| **<ruby><rb>Cepelinai</rb><rt>ツェペリナイ</rt></ruby>**[^Cepelinai] <br />リトアニアに来たら、まずはこれを食べましょう |

[^Cepelinai]: 本当においしい。好きすぎていつも画像を撮り忘れてしまう。画像は以下から拝借：
  [ツェペリナイ - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%84%E3%82%A7%E3%83%9A%E3%83%AA%E3%83%8A%E3%82%A4)

型アサーション（Type Assertion）は、TypeScriptの型を「成形」する機能です[^型アサーション]。
`Vite` で `React` プロジェクト開始時に生成される `src/main.tsx` でも使われています[^Vite]：

[^型アサーション]: 後述するが、型アサーションは型を書き換えるわけではない。
  あくまでコンパイル時に型を主張するのみで、実行には影響しない。
  型アサーションについての説明は以下を参考にしている：
  [型アサーション「as」(type assertion) | TypeScript入門『サバイバルTypeScript』](https://typescriptbook.jp/reference/values-types-variables/type-assertion-as)
  [型アサーション「Type Assertion（型アサーション） | TypeScript Deep Dive 日本語版](https://typescript-jp.gitbook.io/deep-dive/type-system/type-assertion)

[^Vite]: Viteは高速なフロントエンドビルドツールで、はじめてTypeScriptに触る場合にもオススメ：
  [Vite | Next Generation Frontend Tooling](https://vite.dev/)
  また、 `create-react-app` で開始した `React` プロジェクトでも `src/index.tsx` で同様に `as HTMLElement` で型アサーションが使用されている。

```tsx:src/main.tsx
(前略)
reactDom.createRoot(document.getElementById('root') as HTMLElement).render(
    <React.StrictMode>
        <App />
    </React.StrictMode>,
);
```

ここでは、 `document.getElementById()` が `HTMLElement | null` を返すため、
そのままでは `null` の可能性を考慮する必要があります。
そこで、 `as HTMLElement` により返り値の型を `HTMLElement` に成形することで、
`reactDom.createRoot()` に渡す際の型エラーを回避しています[^as]。

[^as]: `as` 以外に、アングルブラケット構文（Angle-bracket Syntax）でも型アサーションを書けるが、この記法はJSXの書き方と似ているため避けられがちで、 `as` がよく用いられる。

:::note warn
型アサーションの `as` は、`import` / `export` 宣言における `as` や、
constアサーション `as const` における `as` とは異なります。
:::

<ruby><rb>Cepelinai</rb><rt>ツェペリナイ</rt></ruby>がすりおろしたじゃがいもを練り上げて形をつくるように、
型アサーションはデータの型を意図した形に成形することを説明しました。

しかし、形を整えるだけでは真の完成には至りません。
次に、型アサーションの具体的な応用テクニックを掘り下げるとともに、
<ruby><rb>Cepelinai</rb><rt>ツェペリナイ</rt></ruby>との共通の注意事項についても詳しく見ていきましょう。



## 型推論が不完全な場面での補完

<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>の厳格なルールの中では、使える食材に制約があります。
この制約のなかで創意工夫を凝らして満足感ある料理を完成させることが重要です。
型アサーションも同じように、型推論が「材料不足」で機能しない場面を補完します。

TypeScriptの型推論は非常に優秀ですが、外部からの入力や、
型情報が不足しているライブラリを扱う際には限界があります。
そのようなときは、型アサーションを適切に使用することで、
以下のように型情報を補完することができます：

```ts
const response: unknown = await fetch("https://api.example.com/data");
const data = response as { name: string; age: number }; // 必要な型を明示
console.log(`名前: ${data.name}, 年齢: ${data.age}`);
```

他にも、DOM要素の取得で `null` の可能性を除外したい場合に利用できます。
以下の例では、型推論では `null` の可能性が排除されない状況を、
非nullアサーション演算子 `!` で補正し、 `null` と `undefined` は無いと主張します：

```ts
const element = document.getElementById("root")!;
console.log(element.innerHTML); // null と undefined の可能性を除外
```



## AssemblyScriptでの事例

また、稀な例ですが、最近 AssemblyScript（以下、AS） を触ったので、
そこで型アサーションを使用した話も紹介させてください。

TypeScriptベースの言語であるASでは、TypeScriptと同様の形式で、
拡張子 `.ts`でプログラムを記述してWebAssemblyを出力することができます[^ASコンパイル]。

[^ASコンパイル]: AssemblyScriptの公式ドキュメントのポータビリティ章に記載がある：
  [Using the compiler | The AssemblyScript Book](https://www.assemblyscript.org/compiler.html#portability)

以下はASで画像をグレースケールにする関数です：

```ts
export function grayscale(imageData: Uint8ClampedArray): Uint8ClampedArray {
    const len = imageData.length;
    const result = new Uint8ClampedArray(len); // 8 ビットの符号なし整数値のビュー

    for (let i = 0; i < len; i += 4) {
        const r = imageData[i];
        const g = imageData[i + 1];
        const b = imageData[i + 2];

        const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b) as u32; // 符号なしに修正

        result[i] = gray;
        result[i + 1] = gray;
        result[i + 2] = gray;
        result[i + 3] = imageData[i + 3];
    }

    return result;
}
```

型アサーションを使用しているのは `gray` を宣言するところです。
`new Uint8ClampedArray()` で生成された `result` は、符号なし整数値の配列です[^Uint8ClampedArray]。
`Math.round()` の返却型 `number` はASでは64桁の浮動小数点数 `f64` に変換されます[^ASのnumber]。

[^Uint8ClampedArray]: `Uint8ClampedArray` は 8 ビットの符号なし整数値のビューで、設定値は0と255その範囲内に制限される：
  [TypedArray | The AssemblyScript Book](https://www.assemblyscript.org/stdlib/typedarray.html)

[^ASのnumber]: 具体的には、 `Number` クラスの静的メンバーとして `F32` や `F64` が定義されており、これらはそれぞれ32ビットおよび64ビットの浮動小数点数型を表す：
  [Number | The AssemblyScript Book](https://www.assemblyscript.org/stdlib/number.html)
  ASの型について詳細が気になる場合は以下を参照されたい：
  [Types | The AssemblyScript Book](https://www.assemblyscript.org/types.html)

:::note warn
残念ながら、ASでは `satisfies` を使えません。
使用しようとすると、次のエラーになります：

<details><summary><code>satisfies</code> を使おうとした場合のエラー</summary>

```
ERROR TS1012: Unexpected token.
    :
 41 │ ) satisfies u32;
    │   ^
    └─ in assembly/index.ts(41,11)

FAILURE 1 parse error(s)
```

</details>
:::

すりおろしたじゃがいもは、水分を落とさなければ生地にはできません。
水分いっぱいの `gray` を、乾いた状態を求める `result` の要素に渡すと、
余計な水分（符号）を含んでいる `f64` を `u32` に型変換しようとするため、
コンパイラは「生地が水っぽすぎると」文句を言い、以下のエラーになります：

<details><summary>型アサーションしない場合のエラー</summary>

```
ERROR AS200: Conversion from type 'f64' to 'u32' requires an explicit cast.
    :
 41 │ result[i] = gray;
    │             ~~~~
    └─ in assembly/index.ts(41,21)

ERROR AS200: Conversion from type 'f64' to 'u32' requires an explicit cast.
    :
 42 │ result[i + 1] = gray;
    │                 ~~~~
    └─ in assembly/index.ts(42,25)

ERROR AS200: Conversion from type 'f64' to 'u32' requires an explicit cast.
    :
 43 │ result[i + 2] = gray;
    │                 ~~~~
    └─ in assembly/index.ts(43,25)

FAILURE 3 compile error(s)
```
</details>

そこで、 `gray` の宣言にて `as u32` で水を絞り切ることで、生地（型）が
正しく成形され、コンパイラという厳しいシェフの検査にも合格するわけです。

[^型アサーションとキャストの違い]: サバイバルTypeScriptによれば、TypeScriptにおける型アサーションはキャストと異なる：
  [型アサーション「as」(type assertion) | TypeScript入門『サバイバルTypeScript』](https://typescriptbook.jp/reference/values-types-variables/type-assertion-as#%E5%9E%8B%E3%82%A2%E3%82%B5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88%E3%81%AE%E9%81%95%E3%81%84)
  キャストは実行時に型を変換するものである。
  しかし、型アサーションは実行時に影響せず、コンパイラに型を主張するのみで、型変換しない。

:::note warn
なお、型アサーションせずに型注釈した場合でも、
同様に明示的なキャストを求められるエラーになりました[^型アサーションとキャストの違い]：

<details><summary>型アサーションせず型注釈した場合のエラー</summary>

```
ERROR AS200: Conversion from type 'f64' to 'u32' requires an explicit cast.
    :
 39 │ const gray: u32 = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
    │                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    └─ in assembly/index.ts(39,27)

FAILURE 1 compile error(s)
```
</details>

:::

型アサーションを使うことが最良のコーディングにつながるとは言いませんが、
ひとまずの問題を解決する手段になることを覚えても良いかもしれません。



## 注意点

<ruby><rb>Cepelinai</rb><rt>ツェペリナイ</rt></ruby>は、じゃがいもと挽肉のハーモニーが最高で非常においしいのですが、
肉を含むため、<ruby><rb>Kūčios</rb><rt>クーチョス</rt></ruby>の厳格な食のルールには適しません。
型アサーションも非常に強力ですが、型安全性を損なう観点から好まれません。
たとえば、以下のような無茶な型アサーションも書けてしまいます：

```ts
const raw: unknown = "じゃがいも";
const potato: number = raw as number; // じゃがいもの実態は文字列であり、数字ではない
```

型アサーションは、あくまでコンパイラに対して「これは大丈夫だ」と主張するだけです。
本当に大丈夫かをチェックしてくれるわけではなく、実行時のエラーの原因になりえます。

以下のダブルアサーションと呼ばれる方法も、同様にコンパイラは通してしまいます：

```ts
const potato: number = ("じゃがいも" as unknown) as number;
```

「水分を絞った」と主張しても、中身は水分たっぷりのじゃがいもです。
もちろん生地の中から具が漏れ出し、エンジニアとしての信頼も失墜します。
中身が不安な場合には、`if` で切り分けするのも一つの手です：

```ts
const raw: unknown = "じゃがいも";

if (typeof raw === "number") {
    const potato: number = raw; // 型チェック後なので安全
    console.log(`安全に数値として使用できます: ${potato}`);
} else {
    console.error("値は数値ではありません");
}
```

そもそも、TypeScriptの型推論は優秀です。
以下のコードでは、そもそも自動的に `potato` が `number` であると推論されます：

```ts
// 型アサーションあり
const potato: number = Math.round(0.299 * 255) as number;

// 型アサーションなし
const potato = Math.round(0.299 * 255); // 自動推論される！
```

芋料理を食べすぎるとお腹に負担をかけてしまうように、
余計な型アサーションを使うと、不要なエラーを招く可能性があります。
コンパイラという料理の相棒を信じましょう。

型アサーションを使う際は「本当に必要か」「中身が正しいか」を確認しましょう。
TypeScriptは、素直に型を注釈するだけで多くの場合きれいに仕上げてくれます。
もし型アサーションを使う時があるとすれば、それはあくまで最終手段として、
**あなたがコンパイラよりもその値や型についてよく理解できているとき**でしょう。



---



# 13. さいごに

最後までお読みいただきありがとうございます。

この12皿が皆さんのTypeScriptメニューに新しい風味を加えられ、
日々の実務に役立つインスピレーションを与えられたなら幸いです。

明日は25日クリスマス、Qiitaアドベントカレンダー2024の最終日です。
@fuchiy さんのDocker環境についての話にも注目しましょう。



---



# 99. 脚注

リトアニア文化についての出典は主に母親ですが、客観性に欠けないよう、
なるべくリトアニア語で書かれた、ソースとなるページをご紹介します。
また、TypeScript機能の表現では、なるべくパブリックなドキュメントを参考にしています。