Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Typiaで配信JSONの安全性を構造的に担保する(TSKaigi2026)

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

 Typiaで配信JSONの安全性を構造的に担保する(TSKaigi2026)

Avatar for RightTouch

RightTouch PRO

May 25, 2026

More Decks by RightTouch

Other Decks in Technology

Transcript

  1. 自己紹介 oiki ( @kn_prg ) 大手 Web 系 → AI

    系ベンチャー → 2022 年から RightTouch TypeScript を軸に、FE から SaaS の 0-1 立ち上げまで いまは プロダクトエンジニア / Hiring Manager 趣味はランニング 2
  2. 自己紹介 oiki ( @kn_prg ) 大手 Web 系 → AI

    系ベンチャー → 2022 年から RightTouch TypeScript を軸に、FE から SaaS の 0-1 立ち上げまで いまは プロダクトエンジニア / Hiring Manager 趣味はランニング 3
  3. 配信 JSON とは RightTouch のプロダクトで、顧客のサイトに配信している JSON ファイル 管理画面で設定 → 顧客サイト上に表示

    3rd party っぽいスクリプトのあるページで Network タブを覗くと、 大抵こういう JSON が配信されている → 誰でも中身を見られる 5
  4. 目指すところ 目的 安全性: 配信 JSON に 不要な値が入らない こと 構造的: ヒューマンエラーで

    容易に混入できない 仕組みであること 基本方針 安全性 → ブラックリストではなく ホワイトリスト 構造的 → 型の二重管理をなくす、変更に確実に気づける 6
  5. Typia とは TS の型をそのまま 実行時バリデータに使えるライブラリ スキーマや builder を別途書く必要なし コンパイル時 に、型に対応する処理コードへ展開

    実行時は素の関数呼び出し → 高速・軽量 validate / is / assert / assertPrune / clone … など多機能 今回の場合、createPrune<T>() という関数を使えば解決できます(完) 11
  6. Typia は何をしているのか 大まかな処理の流れ 1. コンパイル時にASTから typia.xxx<T>(...) を検出 2. TypeCheckerで対象の型を取得 3.

    取得した型を Metadata に変換 4. Metadata からvalidationコードを生成 サンプルの型 type Tree = { name: string; children: Tree[] }; 12
  7. 取得した Type から Metadata に変換 Metadata の構造 { atomics: ["string",

    ...] arrays: [{ value, recursive }] objects: [{ name, properties, recursive }] // ... tuples / aliases / sets nullable: boolean optional: boolean } union は 配列に複数入る だけ null / ? は独立フラグ 二度到達したら recursive: true Tree の Metadata { objects: [{ name: "Tree", recursive: true, properties: [ { key: "name", value: { atomics: ["string"] } }, { key: "children", value: { arrays: [{ value: /* Tree */, recursive: true }] } }, ], }], } 13
  8. Metadataからのコード生成 Metadata { objects: [{ name: "Tree", recursive: true, properties:

    [ { key: "name", value: { atomics: ["string"] } }, { key: "children", value: { arrays: [{ value: /* Tree */, recursive: true }] } }, ], }] } 生成されたコード: typia.validate() (input) => { const errors = []; const $vt = (v, path) => { if (typeof v !== "object" || v === null) { errors.push({ path, expected: "Tree" }); return false; } let ok = true; if (typeof v.name !== "string") { errors.push({ path: `${path}.name`, expected: "string" }); ok = false; } if (!Array.isArray(v.children)) { errors.push({ path: `${path}.children`, expected: "Tree[]" }); ok = false; } else { v.children.forEach((c, i) => $vt(c, `${path}.children[${i}]`)); } return ok; }; return { success: $vt(input, "$input"), errors }; }; 14
  9. 実行方式 Typia の実行方式は2種類ある Transformation モード: tsc のカスタムトランスフォーマーとしてコンパイル時に展開 Generation モード: CLI

    (typia generate ) で事前にファイル生成 → 生成した関数を呼び出す 今回は Generation モードを採用 CI で差分検知することで、型を変更した際の配信 JSON への影響を認知可能 Transformation モードだと自分たちの環境では HMR の速度が大きく劣化 15
  10. 最終的にやったこと 1. 配信 JSON の型から Omit を排除し、Pick するように変更 2. typia

    generate で validation コードを事前に生成 i. validatorの再生成 = 配信 JSON への変更として検知 const pruneSettings = typia.misc.createPrune<ComplexType>(); 3. JSON ビルドロジックにて pruneSettings(data) して不要な値を除去 16
  11. 最終的にやったこと 1. 配信 JSON の型から Omit を排除し、Pick するように変更 2. typia

    generate で validation コードを事前に生成 i. validatorの再生成 = 配信 JSON への変更として検知 const pruneSettings = typia.misc.createPrune<ComplexType>(); 3. JSON ビルドロジックにて pruneSettings(data) して不要な値を除去 比較的シンプルにやりたいことを実現できてハッピー! 17