Mapped Typesを集合と写像の観点から捉える
Mapped Types
Mapped Typesは他の型をもとにオブジェクト型を生成できる機能で、{[P in T]: U}
という構文で表されます。
type Obj = {
num: number
str: string
foo: boolean
}
type MappedObj = {
[PropertyKey in keyof Obj]: string | number
}
// type MappedObj = {
// num: string | number;
// str: string | number;
// foo: string | number;
// }
上のコード例では、Obj
型のプロパティキーをもち、値の型がstring | number
であるようなオブジェクト型MappedObj
が新たに生成されています。
{[P in T]: U}
という構文のT
に使用できる型はプロパティのキーとして指定できる型[1]
の部分型である必要があります。
type MappedObj = {
[PropertyKey in Boolean]: string | number
}
Type 'Boolean' is not assignable to type 'string | number | symbol'.(2322)
集合・写像との関連
Mapped Typesという名前にあるmap は数学では写像を意味します。
写像の定義は次のようになっています。
「
この定義から、Mapped Types{[P in T]: U}
は写像であると捉えることができます。
T
をプロパティキーの集合と捉えると、集合T
の元P
に対して、プロパティ値の型の集合の元U
を一つずつ対応させていますので、この規則は確かに写像であると捉えられると思います。
Mapping Modifiers (マッピング修飾子)
Mapped Typesにはマッピング修飾子というものを指定することで、プロパティをオプショナルなものにしたり、readonly
にすることができます。
readonly
としたい場合には、{readonly [P in T]: U}
のようにreadonly
を追加すればよく、オプショナルにしたい場合には、{[P in T]?: U}
のように?
を追加することで実現できます。
また、{-readonly [P in T]: U}
や{[P in T]-?: U}
のように-
を付加することでreadonly
を外したり、オプショナルでなくすこともできます。
type Obj = {
num?: number
readonly str: string
foo: boolean
}
type MappedObj = {
-readonly [PropertyKey in keyof Obj] ?: Obj[PropertyKey]
}
// type MappedObj = {
// num?: number | undefined;
// str?: string | undefined;
// foo?: boolean | undefined;
// }
この-readonly
や?
はマッピング修飾子(Mapped Modifiers)と呼ばれ、Readonly
型やRequired
型など、ユーティリティ型の実装にも使用されています。
as によるKey Remapping
Mapped TypesにはKey Remapping というプロパティキーに変更を加える機能が備わっています。
{[P in T as V]: U}
という構文で表され、V
に使用できるのはT
と同じくstring | number | symbol
の部分型のみです。
type Obj = {
num: number
str: string
foo: boolean
}
type MappedObj = {
[PropertyKey in keyof Obj as `${PropertyKey}Optional`]?: Obj[PropertyKey]
}
// type MappedObj = {
// numOptional?: number | undefined;
// strOptional?: string | undefined;
// fooOptional?: boolean | undefined;
// }
上のコード例では、keyof Obj
型に含まれるプロパティキーnum
、str
、foo
のそれぞれに対して、プロパティキーをas `${PropertyKey}Optional`
を通してnumOptional
、strOptional
、fooOptional
に変換し(remapping)、値の型Obj[PropertyKey]
をオプショナルにして対応づけています。
Key Remappingは合成写像?
集合keyof Obj
の元PropertyKey
にObj[PropertyKey]
をそれぞれ対応させる写像をkeyof Obj
の元PropertyKey
を`${PropertyKey}Optional`
へ対応させる写像をMappedObj
型はkeyof Obj
しかし合成写像であるならば、最終的に得られる型は{`${PropertyKey}Optional`: Obj[`${PropertyKey}Optional`]}
となってしまい、値の型が存在しないものとなってしまいます。
実際に得られる型{`${PropertyKey}Optional`: Obj[PropertyKey]}
のプロパティは写像を使って表してみると、g(PropertyKey): Obj[PropertyKey]
という対応になっています。
これは、PropertyKey
に写像
プロパティキーのみが写像
Discussion