TypeScriptの型で使われる-?や-readonlyの意味
TypeScriptの型パズルを見ていると-?や-readonlyという記号を見かける。非常にググラビリティが悪い。
この-という記号は、続く?やreadonlyを除外するという役割がある。
公式ドキュメントではreadonly mapped type modifiers and readonly arrays | TypeScript 3.4で軽く触れられている程度。
Required<T>を使う場合
?というオプショナルな状態を除外するためにRequired<T>を使うと以下のようになる。
type Item = {
optional?: string
require: string,
children: {
optional?: string
require: string
}
}
// children は optional のまま
type RequiredItem = Required<Item>
ネストされたオブジェクトに対してはRequired<T>が適用されない。
DeepRequired<T>を使う場合
ネストされたオブジェクトに再帰的にRequired<T>を適用することもできる。ただし、以下のようにchildrenがRequired<{...}>という形になってしまう。
type DeepRequired<T> = T extends object
? Required<{ [K in keyof T]: DeepRequired<T[K]> }>
: T
type RequiredAll = DeepRequired<Item>
// {
// optional: string;
// require: string;
// children: Required<{
// optional?: string | undefined;
// require: string;
// }>;
// }
-?を使う場合
-?を使って?(オプショナル)を除外すると、Required<T>が現れないプレーンなオブジェクトの型になる。
type DeepRequired<T> = T extends object
? { [K in keyof T]-?: DeepRequired<T[K]> }
: T
type RequiredAll = DeepRequired<Item>
// {
// optional: string;
// require: string;
// children: {
// optional: string;
// require: string;
// };
// }
-readonlyの場合
-readonlyも同様にreadonlyを除外するために使える。
type Item = {
readonly val: string
}
type Editable<T> = T extends object
? { -readonly [K in keyof T]: Editable<T[K]> }
: T
type EditableAll = Editable<Item>
// {
// val: string
// }