こんにちは!フロントエンドの現場で日々コードと格闘しているチーフアーキテクトです。
TypeScriptを触り始めると、必ず一度は「あれ、なんでこれで通っちゃうの?」という瞬間に遭遇しますよね。実はTypeScriptの優しさは、時に「お節介」にもなり得ます。
今日は、そんなTypeScriptの「構造的部分型」という少し変わった性格を、「ブランド(Brand)パターン」という必殺技でコントロールするお話をしましょう。初心者の方でもスッと腑に落ちるよう、身近な例えで解説していきますね。
—
1. なぜ「なんでもアリ」になってしまうのか?
TypeScriptは「中身が同じなら同じものとみなす」という性格をしています。これを「構造的部分型」と呼びます。
例えば、こんな状況を想像してください。
type UserId = string;
type ProductId = string;
const user: UserId = “user_01”;
const product: ProductId = “user_01”; // 本来は別のものなのに!
// 中身がどちらも文字列だから、TypeScriptは怒らない
const doSomething = (id: UserId) => { / … / };
doSomething(product); // !!エラーにならない!!
「商品ID」を渡すべきところに「ユーザーID」を渡してしまったら、バグの温床ですよね。でもTypeScriptは「どちらも文字列じゃん、OKOK!」とスルーしてしまいます。これが現場で意外と怖い「型は合っているのに中身が違う」という事故です。
—
2. そこで登場!「ブランド(Brand)パターン」
これを防ぐのが「Nominal Typing(名前による型判定)」のシミュレーションです。
イメージとしては、「中身はただの文字列だけど、わざわざ『これはユーザーIDというラベルがついた箱です』と刻印を押す」ような作業です。
刻印を押すコード
// インターフェースに「__brand」という架空の刻印を忍ばせます
type UserId = string & { readonly __brand: “UserId” };
type ProductId = string & { readonly __brand: “ProductId” };
// 刻印を押すための小さな関数(キャストを使います)
const toUserId = (id: string) => id as UserId;
const toProductId = (id: string) => id as ProductId;
const myUser = toUserId(“user_01”);
const myProduct = toProductId(“user_01”);
function deleteUser(id: UserId) {
console.log(“ユーザーを削除します”, id);
}
// 試してみよう!
deleteUser(myUser); // OK!
// deleteUser(myProduct); // !!ここでTypeScriptが激怒してくれる!!
// 「’__brand’ プロパティがないのでダメですよ!」と教えてくれます。
見てください!たったこれだけで、TypeScriptが「それはユーザーIDじゃないからダメ!」と止めてくれるようになります。
—
3. なぜこれが「現場の知恵」なのか?
「いちいち刻印を押すの、面倒じゃない?」と思うかもしれません。確かに、小さなプログラムなら不要です。
でも、数万行を超えるような大規模なWeb開発では、この「間違いを未然に防ぐコスト」が、後の「デバッグに費やす膨大な時間」を救ってくれるんです。
- APIからのレスポンスを扱うとき:バックエンドから来たデータに、この刻印を押してからドメイン層に渡す。
- 関数の引数ミスを防ぐ:IDの種類が複数ある複雑な画面で、型安全をガチガチに固める。
これは、現場のアーキテクトが「自分やチームメイトが将来ミスをしないための仕掛け」としてよく使う手法なんですよ。
—
4. 最後に:怖がらなくて大丈夫です
最初は「`& { readonly __brand: … }` って何?」と難しく感じるかもしれません。でも、大丈夫。これは「TypeScriptに、僕らの意図(これはユーザーIDなんだ!)を伝えるための魔法の呪文」だと思ってください。
まずは、自分のコードの中で「これとこれは混ざっちゃいけないな」と思うIDの型定義に、一つずつ刻印を押すところから始めてみてください。
最初は手探りでいいんです。その小さな積み重ねが、いつかあなたの書くコードを「世界で一番堅牢で美しいコード」へと進化させてくれますよ。
もし途中でつまずいたら、いつでもまたここへ戻ってきてくださいね。応援しています!

コメント