オブジェクトのvalueの型によってプロパティを削除する型定義
TSのオブジェクトでvalueの型が特定の型だった場合にそのプロパティを削除したオブジェクトの型を取得したかった。
たとえばあるオブジェクトの型からstringのプロパティのみを排除した型を取得したいみたいなこと。
これを取得する型を実装しようとしてちょっとハマったのでメモ。
例として以下のようなオブジェクトを考える
foo1, foo2はいずれもstringでそれ以外のbarとbazはそれぞれnumberとbooleanであるSampleというオブジェクトの型を考える。
この型からstring型であるプロパティを全て取り除いた以下のような型を取り出したい。
これが実現できるExcludeStringという型を自作するのが今回の目標。
*conditional types, mapped typesなどのTSの機能についてはここでは理解している前提で記述する。
最初mapped typesを使ってこんな感じで実装したけど上手くいかなかった
value部分がneverになってしまってオブジェクトのプロパティそのものは存在してしまう
失敗例の通り、valueの部分でconditional typesを使ってもkey自体は残る。
なので、conditional typesを使ってvalueを判定するが、keyそのものを取り入れるかどうかを判定したい。
どうしようかと知恵を絞った結果、asによるkeyのremappingを使うと綺麗にできた。
(asによるremappingはTS4.1以降でしか使えない)
こうすることで、判定する対象はvalueだが実際にその結果を受ける場所はkeyというようにすることができる。
ただしvalueの型が1つだけの時はこれでも良かったが、以下のunion型の時は削除されなかった。
結果から推測するに、string | number
のユニオン型の時、T[K] extends string
は必ずしも満たされないということなのかなと認識している。
間違っていたら教えてください。
なので、もしユニオン型の時でも対象としたい場合はstring extends T[K]
としてやれば条件が満たされる。
ちなみに今回はstringに限定したが、もう一つ型パラメータを受け取るようにすれば任意のvalueの型を持つkeyを削除する汎用的な型を作ることもできる。
やりたいことはこれで達成できたのだが、ちょっと遊んでみる
オブジェクトでオプショナルなキーを持つ型をVS Code上でホバーすると、オプショナルな部分はstring | undefined
みたいになる。
なので今回自作したやり方でundefinedを持つプロパティを削除するようにすればオプショナルなキーを削除する型を作れるのかなーと思いやってみた。
見事オプショナルなキーfooは削除することができた。
ただ、これだと以下のようなオプショナルなキーではないが、ユニオン型としてundefinedを持つプロパティも削除されてしまう。
こういうケースでも削除したい場合は問題ないが、オプショナルなキーを削除するというニュアンスとは異なったものになってしまう。
このやり方だとオプショナルなプロパティとオプショナルではないがundefinedをユニオン型で持つプロパティを区別できなさそう。
(できるやり方があったら教えてください!)
どうすればよいかまたまた知恵を絞った結果、元のオブジェクトとRequiredしたオブジェクトを比較してvalueが異なる(= undefiendが付与されている)ものをオプショナルなキーと判断して削除すればいけた。
(Requiredしたオブジェクトを勝手に初期値としてUに設定する)
綺麗にオプショナルなキーだけが消せた!
満足!!
conditional typesとmapped typesがわりと自由に使えるようになってきた。
この辺使えばやりたいことはある程度できそう。
取得したい型が取れると気持ちいい
literal typesもこれらに組み合わせて使えるようになってる気はするんだけど、如何せん実務でliteral typesをきちんと使おうという場面を見出せていない。
ここで使えるなという場面を見出す観察眼がまだ足りてない気がするのでその辺をなんとかするのが今後の課題。
https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#key-remapping-via-as