Slide 1

Slide 1 text

配列にまつわる型検査をしたら思っ たより大変だった話 2025/02/05 shibuya.ts Kenta TSUNEMI 1

Slide 2

Slide 2 text

名前: つねみ @tocomi0112 🐦 所属: テックタッチ株式会社 役職: フロントエンドエンジニア 趣味: お笑い / 野球観戦 / 競馬 / ポーカー 2 自己紹介

Slide 3

Slide 3 text

const ATTRS = ['id', 'type', 'class'] as const; という値があったときに、ある配列がこれらの要素を 過不足なく 順不同で 含んでいるということを担保する型を作りたい。 3 実現したかったこと

Slide 4

Slide 4 text

const ATTRS = ['id', 'class', 'type'] as const; type SugoiKata = ??? const ok1: SugoiKata = ['id', 'class', 'type'] // const ok2: SugoiKata = ['type', 'id', 'class'] // 順不同は許容 const ng1: SugoiKata = ['id', 'class'] // type が不足 const ng2: SugoiKata = ['id', 'class', 'type', 'for'] // for が余分 4 実現したかったこと

Slide 5

Slide 5 text

値の不足を検知できない const ATTRS = ['id', 'class', 'type'] as const; type Sugokunai = (typeof ATTRS)[number][]; // ^? type Sugokunai = ("id" | "class" | "type")[] const ok1: Sugokunai = ['id', 'class', 'type'] // const ok2: Sugokunai = ['type', 'id', 'class'] // const ng1: Sugokunai = ['id', 'class'] // 不足が検知できない const ng2: Sugokunai = ['id', 'class', 'type', 'for'] // : 期待通り : 期待通りでない 5 配列の型を作ってみる

Slide 6

Slide 6 text

順番の違いや重複でエラーになってしまう const ATTRS = ['id', 'class', 'type'] as const; type Sugokunai = typeof ATTRS; // ^? type Sugokunai = readonly ["id", "type", "class"] const ok1: Sugokunai = ['id', 'class', 'type'] // const ok2: Sugokunai = ['type', 'id', 'class'] // 順番が固定される const ng1: Sugokunai = ['id', 'class'] // const ng2: Sugokunai = ['id', 'class', 'type', 'for'] // : 期待通り : 期待通りでない 6 タプルにしてみる

Slide 7

Slide 7 text

const ATTRS = ['id', 'class', 'type'] as const; type Diff< T extends readonly string[], U extends readonly string[] > = | Exclude | Exclude type HasSameElements< T extends readonly string[], U extends readonly string[] > = Diff extends never ? true : false const ok1 = ['id', 'class', 'type'] as const; true satisfies HasSameElements 7 どうにか頑張ってみる

Slide 8

Slide 8 text

過不足がなければ Diff は never になる const ATTRS = ['id', 'class', 'type'] as const; type Diff< T extends readonly string[], U extends readonly string[] > = | Exclude | Exclude type A = Diff<['id', 'class', 'type'], ATTRS> // ^? type A = never type B = Diff<['id', 'type'], ATTRS> // ^? type B = "class" type C = Diff<['id', 'type', 'class', 'for'], ATTRS> // ^? type C = "for" 8 Diff は何をしているか?

Slide 9

Slide 9 text

Diff の結果を boolean に変換した上で検査する // Diff の結果を boolean に変換する型 type HasSameElements< T extends readonly string[], U extends readonly string[] > = Diff extends never ? true : false // HasSameElements が false = 過不足がある場合は型エラー true satisfies HasSameElements< ['id', 'class', 'type'], typeof ATTRS > // OK true satisfies HasSameElements< ['id', 'class'], typeof ATTRS > // 型エラー 9 satisfies での検査

Slide 10

Slide 10 text

期待通りの結果を得られた 10 結果...

Slide 11

Slide 11 text

const attrs = ['id', 'class', 'type'] as const; true satisfies HasSameElements なんか微妙 type SugoiKata = ... const attrs: SugoiKata = ['type', 'id', 'class']; こういう形にしたかった... もっとシンプルにできないものか アイデアがあれば教えてください! 11 できたけど...

Slide 12

Slide 12 text

ありがとうございました! 12 おわりです