オブジェクトのキーの配列を過不足なく型チェックする
オブジェクトのキー一覧を配列として扱いたい時に、全てのキーが過不足なく配列の中に格納できているかどうかを型チェックする仕組みが欲しかった。コードで書くとこんなことがしたかった
これができるような型システムを自作したのでまとめておく
オブジェクトのキー一覧を配列として扱う時にそれらのキーを配列にしたかった。オプショナルのキーも含めて配列に入れたい
単純に上記のように書けばいい
ただ、Fooに新しくプロパティを追加した時にfooKeysにも追加しなければいけないけど、そのままだと追加し忘れる可能性がある
なので過不足ない配列にしたくて、変なものが混ざってたり必要なものが不足してたりしたらエラーを出すようにしたかった。
そうすれば追加し忘れていたらCIで落ちるので追加し忘れを防げる
適当なオブジェクトを定義してObject.keys
とすればkeyを取れるからそれでいける?
と思ったけどObject.keys
ではstring[]
になるのでこの方法はダメだった
ぱっと思いついた方法だと以下の通り
ただこれだと、Fooに存在しないキーを含めるとエラーになってくれるが、本来存在するはずのbar
やbaz
が配列の中に入っていなかったとしてもエラーになってくれない
(keyof Foo
はユニオン型なのでそれを満たしてさえいればエラーにならない)
そうすると結局Fooに新しくプロパティを追加した時にこのコードに追加漏れが発生する可能性があった
どうもサクッとできなさそう…
最近TypeScriptの型について色々深く勉強したのでせっかくなのでそれを活かそうと思い、ちゃんと型チェックをしてエラー検知をする仕組みを自作することにした
(オブジェクトのキーをタプルの型にしてアノテーションする方法も一応できたんだけど、キーの数の順列数のユニオン型のタプルを計算することになってしまって、計算量が膨大になったので却下した)
アプローチを変えて定義した配列をユニオン型にしてそれがkeyof Foo
と一致するかどうかを確認することにした
まず、配列を定義する。この時にas const
を使ってタプル型にする。
そうしないとfooKeysがstring[]
になってしまうので
これをユニオン型にするために以下のようにする。
次にこれがkeyof Foo
と一致するかどうかを確認して、一致しない場合はエラーになるようにしたい
まず一致するかどうかを確認するための道具が必要
詳細はリンク先参照だがこうすればできる
[Feature request]type level equal operator · Issue #27024 · microsoft/TypeScriptSearch Terms Type System Equal Suggestion T1 == T2 Use Cases TypeScript type system is highly functional. Type level testing is required. However, we can not easily check type equivalence. I want a...github.com
んーよくこんなの思いつくなーすごい。笑
Equals
に食わせた2つの型パラメータが一致していればtrueを返すし、一致していなければfalseを返す
ありがたくこれを使わせてもらうとして、あとは上記のEquals
の結果がfalseの場合のみ型チェックでエラーになるようにすればよいので、以下のような型を作る
Expect
はtrueしか受け取らない。
これをEquals
と組み合わせて使うことでEquals
に食わせた2つの型パラメータが異なる時はfalseとなり、Expectのところでタイプエラーが出るようになったので、これでCIで検知することができるようになった
全てを合わせると以下のようになる
この状態で例えばFoo
に別のプロパティを追加してみるとエラーになる
このTypeCheckFooKeys
自体はどこかで使うわけではないのでTSが定義したのに使ってないぞってエラー出すけど、これはignoreする記述書くか、exportしてしまえばok
ただ置いておくだけでチェックしてくれるようになった
オブジェクトの一部のキーは不要とかの場合はkeyof Foo
から不要なプロパティだけExcludeしてやればok
もっといい方法があるかもしれないが今回は必要な機能を自作できたので満足
最近TSの型パズルにハマっているのでどうすれば必要な型を得られるかなーと考えて作るのが楽しい
TSは本当に表現力豊かだなあと思う
[Feature request]type level equal operator · Issue #27024 · microsoft/TypeScriptSearch Terms Type System Equal Suggestion T1 == T2 Use Cases TypeScript type system is highly functional. Type level testing is required. However, we can not easily check type equivalence. I want a...github.com