問題 & 解答
二つの引数T
、K
をとり、K
が指定されていればT
のそのプロパティを、指定されていなければすべてのT
のプロパティを読み取り専用に変換するMyReadonly<T, K>
を実装する。
「readonly
なK
で指定されているプロパティ & T
に含まれるK
以外のプロパティ」を目指して作っていきます。
readonly
なK
で指定されているプロパティを作る
1. これが通常のReadonlyの実装です。
このままだとK
の値がreadonly
にならないので、mapped typesでぐるぐるするところを変えます。
これによりK
に渡されたプロパティはreadonly
になります。
T
に含まれるK
以外のプロパティ
2. これだけだとK
に含まれるプロパティしか含まれていません。そのため、K
に渡されなかったプロパティの型を取得する必要があります。これはT
の中からK
に該当するプロパティを除いたものです。
例えば以下のようなT
とK
を渡すことを考えます。
最終的にMyReadonly2
に期待するのは次のような形式なので、name: string
を取り出せれば良いはずです。
これは昨日出てきたOmit
(組み込みの型の方です)を使用して、Omit<T, K>
の形式で取り出すことができます。
3. 1と2を合体
これらをインターセクション型で繋ぎこむと、解答を得ることができます。
K
のデフォルト値を設定する
4. …と思ったらまだエラーが出ています。この型はK
を省略可能なのでそこでひっかります。K
は参照されるので何かしらの値を入れておく必要があります。
TypeScriptは型引数にデフォルト値を取ることができます。
https://typescriptbook.jp/reference/generics/default-type-parameter
今回は「K
を指定しなかった場合、すべてのプロパティがreadonly
になる」ので、K
には「T
のプロパティすべて」を設定します。
他の人の解答を見ていたら、Omit<T, K>
の部分を& T
で繋ぎ込んでいる解答もあったのですが、これではだめでした。
感想
う〜ん、最後のインターセクション型の挙動についてはドキュメントをざっと読んだのですが、期待する記述は見つけられませんでした。readonly
だけならいいんですが、他にも自分が理解できていない部分があると怖いです。