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