Content Collectionsのスキーマ、TypeScriptの型としても使い回せることに気づいた話
// 記事のポイント
- Zodスキーマで定義した形を、TypeScriptの型としてコンポーネント側で手書きし直すと、更新のたびにズレるリスクがある
- 実際に、手書きの型がスキーマの最新の形と食い違っているのを見つけた
- CollectionEntry<'コレクション名'>['data']から型を直接取り出せば、二重管理をなくせる
前にContent Collectionsの基本を紹介したと思うんだけど、実はあの後、地味だけど大事な型のズレを見つけて直したんだよね。今日はその話をしていくよ〜。
型のズレ?
こめの中では、クッキーの型からはみ出ちゃった生地みたいなイメージなんだけど、合ってる?
同じ形を何度も手書きしていた
このブログの記事データは、`src/content.config.ts`のZodスキーマで形が決まっている。会話のセリフ1つひとつには、話者やテキストのほかに、任意で画像や商品リンクを付けられるようになっており、リンクの中にはショップごとのボタンを並べる`shops`のような配列も入っている。この構造をTypeScriptの型としてコンポーネント側でも使いたい場面が何度も出てきた。
同じ形をあちこちに書き写すの、わりと手間だし、うっかりズレちゃいそうだね〜。
そうなんだよね。最初の頃は、記事を表示するコンポーネントごとに`interface Turn`みたいな型を自分で書いてたよ。似たような形の型が、3か所くらいに別々に存在してた。
手で何回も書き写してたら、そのうち1個くらい書き忘れちゃうこともあるんじゃない?
実際に見つかったズレ
実際にあった。記事の会話に添える`link`には、商品画像のURLを入れる`image`という項目がスキーマ側に存在していたが、あるコンポーネントの手書きの型にはこの項目が抜けていた。スキーマとは無関係に書かれた型だったため、TypeScript自体はこのズレを教えてくれない。見た目には出ないまま埋まっていたズレだった。
それって、実際の記事の表示に影響が出てたってこと…?
そこまでではないよ、たまたま、その項目を使う記事がまだ無かっただけ。でも、これから使おうとしたタイミングで気づかずにハマってた可能性はあったよね。
関連するコードを触ってたときに見つけたんだけど、ちょっとヒヤッとしたよ。
スキーマから型を直接取り出す
直し方は単純で、手書きの型をやめて、スキーマから型そのものを取り出す形にした。`CollectionEntry<'blog'>['data']['conversation'][number]`と書くと、記事データの会話1ターン分の型がそのまま手に入る。画像やリンクのように任意項目になっている部分は、`NonNullable<...>`で「値が入っている場合の形」だけを取り出せる。
それって、スキーマさえ合ってれば、こめたちが写し間違えることはもう起きないってこと?
手書きの型自体をなくしちゃえば、そもそも書き間違えようがないもんね〜。
そういうこと! スキーマは実行時のチェックだけじゃなくて、TypeScriptの型の元にもできる、っていうのが今回の一番の学びだったかな。同じ形をあちこちで手書きしてる心当たりがあったら、一度スキーマから取り出せないか見直してみるといいかも。
// 記事のまとめ
- スキーマは実行時のチェックだけでなく、TypeScriptの型の元にもできる
- 同じ形を複数箇所で手書きしている心当たりがあれば、一度スキーマから型を取り出せないか見直す価値がある


