Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

TSKaigi Hokuriku - TypeScriptによる静的データガバナンス

Avatar for teamLab teamLab PRO
December 08, 2025

TSKaigi Hokuriku - TypeScriptによる静的データガバナンス

このスライドはTSKaigi Hokurikuの登壇に使用したスライドのアーカイブになります。

チームラボでは、フロントエンドエンジニアを募集しています。
少しでも、チームラボにご興味をお持ち頂けましたら、採用ページをご覧頂けますと幸いです。
https://www.team-lab.com/recruit/

TSKaigi Hokurikuは、TypeScriptの国内最大級技術カンファレンス「TSKaigi」の北陸地域版で、2025年11月に金沢で開催されたイベントです。
https://hokuriku.tskaigi.org/

Avatar for teamLab

teamLab PRO

December 08, 2025
Tweet

More Decks by teamLab

Other Decks in Technology

Transcript

  1. © teamLab Inc. 定義 • 基本4情報 ◦ ⽒名、 所、⽣年⽉⽇、性別 •

    デジタル識別⼦ ◦ メールアドレス、電話番号、Cookie ID • 要配慮個⼈情報 ◦ 病歴、思想信条、クレジットカード情報 4
  2. © teamLab Inc. 個⼈情報取り扱いに潜むリスク • 外部送信リスク ◦ GA等への規約違反連携 • ログ出⼒

    ◦ 平⽂での保存‧閲覧 • 意図せぬ伝播 ◦ 不要な画⾯への表⽰  重⼤なコンプライアンス違反と信頼の失墜 5
  3. © teamLab Inc. PII型の定義 Branded Typesの⼿法を使ってただのdataと個⼈情報を区別する 7 // PIIであることを識別するためのユニークなシンボル(外部からは見えない) declare

    const PiiBrand: unique symbol; // T型に「これは個人情報である」というタグを付与した型。 type PII<T> = T & { [PiiBrand]: true };
  4. © teamLab Inc. 値の定義 // --- データ定義 --- interface User

    { id: number; name: PII<string>; // 普通の情報 email: PII<string>; // 文字列のPII hobby: string } // ユーザデータ定義 const user: User = { id: 1, name: "Takagi Katsuya", email: "[email protected]", hobby: 'food' } 8
  5. © teamLab Inc. ログ関数 //再帰的に型をチェックし、PII型が含まれていれば never (エラー) に変換する型 type EnsureNoPII<T>

    = T extends PII<unknown> ? never : T extends object ? { [K in keyof T]: EnsureNoPII<T[K]> } // オブジェクトなら中身を再 帰チェック : T; // それ以外(プリミティブなど)はそのまま通す // 引数 data は、PIIを含まない形式 (EnsureNoPII<T>) でなければならない function safeLogger<T>(data: T & EnsureNoPII<T>) { // 実行時は普通のオブジェクトなので、そのまま出力される console.log('[LOG]', JSON.stringify(data, null, 2)); } 9
  6. © teamLab Inc. 鉄壁の守り safeLogger(user); // -> エラー: 'email', 'salary'

    が never に割り当てられません safeLogger({ email: user.email }); // -> エラー: 型 'PII<number>' を 'never' に割り当てられません safeLogger(user.hobby); // -> OK 10
  7. © teamLab Inc. マスク関数の実装 declare const MaskedBrand: unique symbol; //

    安全に処理された文字列。この型を持っていればログ出力が許可される。 type Masked = string & { [MaskedBrand]: true }; // PII型を受け取り、加工して、唯一の正規ルートとしてMasked型を返す。 function mask<T>(_value: PII<T>): Masked { // 簡易的なマスク処理 const maskedResult = '********' // Maskedとして返す return maskedResult as Masked; } 12
  8. © teamLab Inc. 安全なログ出⼒の実現 safeLogger({ id: user.id, // 個別にマスク処理を通す name:

    mask(user.name), // "********" (Masked) email: mask(user.email), // "********" (Masked) hobby: user.hobby }); // -> OK 13