TypeScript の型システムは、それ自体がチューリング完全に近い小さな言語だ。 値の世界とは別に、型の世界で計算することができる。
条件型とインフラ
条件型 (extends ? :)と infer を組み合わせると、型から型を取り出せる。
type ElementOf<T> = T extends readonly (infer U)[] ? U : never;
type A = ElementOf<string[]>; // string
type B = ElementOf<[1, 2, 3]>; // 1 | 2 | 3再帰で計算する
タプルの長さを使えば、型レベルで自然数を表現できる。
type Length<T extends readonly unknown[]> = T["length"];
type Push<T extends readonly unknown[], V> = [...T, V];
type One = Push<[], unknown>; // [unknown]
type Two = Push<One, unknown>; // [unknown, unknown]
type N = Length<Two>; // 2Zod との接続
型レベルの厳密さは、境界(API レスポンスなど)では崩れる。
そこは Zod でランタイムに検証し、z.infer で静的型へ橋渡しする。
import { z } from "zod";
const Post = z.object({
title: z.string(),
tags: z.array(z.string()),
});
type Post = z.infer<typeof Post>; // { title: string; tags: string[] }型は宇宙だ。だがその宇宙の縁では、ランタイムの検証が現実を守っている。