やりたいことはこう
type User = { id: number, name: string } const value = 'id'
上記のコードでvalue
の値がUser
型のkeyのいずれかであるかを判定したい。
つまり、上記の場合id
はUser
型のkeyに存在しているのでtrue
になるif文を書くようなイメージ。
結論
あまり綺麗に書くことはできない。
そもそもTypeScriptの型はコンパイル時だけの存在で実行時には消えるため、型と実行時の値を比較することはできない。
そのため比較したい場合User
型のkeyの情報を何らかの値として保持する必要がある。
パターン1
一番愚直にやるならこんなイメージ。
const userProperties = ['id', 'name'] type User = { id: string; name: string } const value = 'name' if (userProperties.includes(value)) { console.log('true') }
わかりやすいけどUser
型にid
とname
があるよという情報が二重管理されていて冗長な印象を受ける。
userProperties
とUser
型には繋がりがないので、今後User
型にphone: number
を追加する場合、値と型のどちらかに追加を忘れてもエラーで検出できない可能性がある。
パターン2
こんな書き方だともう少しスマートに見える。
const keys = ['id', 'name'] as const type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType[number] type User = { [k in ArrayElement<typeof keys>]: string } const user: User = {id: '1', name: 'hoge'} const value = 'name' if (keys.includes(value)) { console.log('true') // true }
先ほどのkeyの二重管理は無くなったが、これではUser
型のプロパティが全て同じ型になってしまうため(上記だと全てstring
)、使える場所が相当限られる。
パターン3
keyを2回記述することは避けられないが、ここまでやると一応keyの追加漏れもある程度エラーで検知できる。
type User = { id: number; name: string } class UserClass implements User { id = 0 name = "" } const userProperties = Object.keys(new UserClass()) console.log(userProperties) // (2) ['id', 'name'] const value = 'name' if (userProperties.includes(value)) { console.log('true') // true }
もしphone: number
の型情報をUser
のみに追加した場合、UserClass
がUser
を満たさなくなるのでエラーとなる。
逆にUserClass
だけに追加した場合、userProperties.includes('phone')
とした時にエラーとなる。
ただ一見して何がやりたいのかよくわからないコードになるので、現実的ではないと思う。
End
そもそもこれってできるんだっけ、っていうところから色々深掘りしてしまった。