📧

一日一処: もっと低コストに文字列のTypeScriptの型をつくりたい

2024/01/26に公開

TypeScriptの型

そこそこハゲてしまうようなテーマだと思う。特に、複雑な関数等の場合、型定義が非常に長くなったり、整理しようとしても逆に名前が分かりづらくなったり、意味不明な状態に陥ってします。

一定の文字列の型

ある特定の文字列が決まっている場合、以下のように型定義を行うことが多のではないだろうか。

type ValueName = 'name' | 'address'

また、このような方を用いて、オブジェクトの型を定義することもあるだろう。

type UserValue = { [k in ValueName]: string }

さらに、特定の要素にアクセスする際は、ブラケットに渡す文字列も型に沿っていないと怒られる。そのため、このように書くだろう。

type ValueName = 'name' | 'address'
type UserValue = { [k in ValueName]: string }

const key: ValueName = 'name'
const userValue: UserValue = { name: 'Alice', address: 'alice@example.com' }
console.log(userValue[key])

これくらいならまだいいが、すべての値を取り出したいときはこうなることもあるだろう。

type ValueName = 'name' | 'address'
type UserValue = { [k in ValueName]: string }

const keys: ValueName[] = ['name', 'address']
const userValue: UserValue = { name: 'Alice', address: 'alice@example.com' }
keys.forEach((k) => console.log(userValue[k]))

このようなプログラムになった時、やはり気になるのか、オブジェクトのキーである文字列が何度も登場していることだ。TypeScriptの思想と仕様上ありえないが、本当はこうできるとありがたい。

// 型定義を処理そのものに用いることはできないが理想としたい処理
ValueName.forEach((k) => console.log(userValue[k]))

配列から型を定義したい

そこで、キー名を定める値を先に設定し、その後に型を定義すると、記載量は減少する。若干のわかりづらさはあるが、何度も同じことを書いたり、修正箇所が増えることを考えれば、許容範囲だろう。

const keys = ['name', 'address'] as const

type ValueName =  typeof keys[number]
type UserValue = { [k in ValueName]: string }

const userValue: UserValue = { name: 'Alice', address: 'alice@example.com' }
keys.forEach((k) => console.log(userValue[k]))

少しスッキリしたように見える。その他の型定義の方法については、ドキュメントに記載がある。

Discussion