今更ながら頑張るTypeScript(strict: true
)をやろうという事になったのでメモ書きをつらつらと書いていきます。
今までやりたい放題anyをぶちこんでた無法地帯に秩序をもたらすべくstrictに対応させていくのはなかなか大変です。
既存のコードに型を付けていく作業中ですがなかなかサクッとやりたいことに対応するコードが出てこない+なんども同じようだけど少し違うみたいな状況で
そのたびにあれ?これってこうだっけ?と調べてということをやっている気がしてきたので最小なパターンで実装例をアウトプットしてあとで思い出しやすくします。
ということで完全にメモ書きです。
今回はConditionalTypeについて
Conditional Type
TypeScript2.8から導入された機能
名前の通り型の定義に条件分岐を含めることができるというものです
特定の連想配列から特定の型の値を持つキーのリストを取得する
連想配列に対して[]
でstringの文字列を指定したときに出てくるno index signature
系の話ですね
添字に渡すキーを限定しないとエラーになるわけですが、こんなのめちゃくちゃよくやってるよ!
「連想配列に含まれるキーすべて」であればkeyof
のみで事足りるのですが
今回はその中でも特定の型の値を持つキーのみを抽出したい場合のパターンです
サンプルでコードを書いてみました。
interface Hoge { hoge1: number; hoge2: string; } interface Fuga { id: number; name: string; } interface SampleRow { a: Hoge; b: Fuga; c: string; d: boolean; e: string; } // Kは型引数Tのキー T[K] = value の値がFugaかどうかを見てマッチする場合はK. しない場合はneverのキーのUnionType type FugaPropertyNames<T> = { [K in keyof T]: T[K] extends Fuga ? K : never } [keyof T]; const row: SampleRow = { a: { hoge1: 1, hoge2: 'hoge' }, b: { id: 1, name: 'fuga' }, c: 'piyo', d: true, e: 'e' }; const field: string = 'b'; const fugaKeys: FugaPropertyNames<SampleRow>[] = ['b']; const isFugaKeys = (key: string): key is FugaPropertyNames<SampleRow> => { return fugaKeys.some(k => k === key); }; // field: string if (isFugaKeys(field)) { // field: "b" const value = row[field]; }
コメントに書いてありますがSampleRow
の中でFuga
という型のvalueを持つキーのリストを定義させることで
タイプガードの中ではfield
の値が"b"
のみとなります
type FugaPropertyNames<T>
でやりたいことはできてしまっているのですが確認の為にいくつかコードを書き換えてみます
- fugaKeysに要素を足す
- タイプガードの前で
row[field]
を書く - aの方をFugaに変更してみる -> '"a"|"b"' になる
意図通りの動作になりました
今回の場合だとFuga
型のデータのときのみなにかやりたい場合に使えるかと思います
最初Object.entries
やObject.keys
,Object.values
などを使ってできないかも考えたのですがそれぞれループの中での型がany
になってしまうので結局その中でタイプガードを書くことになりそうだったため別のアプローチを試みてみました。