TL;DR

構造が同じ型に、目印(ブランド)となるプロパティを交差型で付け足して区別する手法。構造的型付けの TypeScript で、名前的型付けを実現するために使う。

なぜ必要か

TypeScript のハンドブックによると、TypeScript の型の互換性は構造的部分型に基づいており、型のメンバーだけで型どうしの関係を決める。そのため、ユーザ ID と商品 ID のように意味は違うが構造は同じ string を、型だけでは区別できない。Effective TypeScript 項目64 によると、Branded Types を使えば、こうした型を区別して定義できる。また「ソート済みである」のような、型システムだけでは表現できない性質も表せる。

仕組み

Effective TypeScript 項目64 では、実装の方法として、オブジェクト型のプロパティ、プライベートフィールド、文字列の enum、unique symbol 型の 4 つを挙げている。unique symbol を使う方法では、ブランドのシンボルを export しないので、ブランド付きの値を得るには型アサーションか補助関数を通す必要がある。

例

declare const brand: unique symbol
export type UserID = number & { [brand]: "user_id" }

注意点

ブランドのキーを 1 つにする実装では、ブランドをネストした型が never になる。キーが同じなので、ブランドの値が 2 つの文字列を同時に満たさなければならず、そのような値は存在しない。Branded types をネストさせたい では、ブランドのキーを型ごとに分けてこれを避けている。

関連

出典