type-challenges 14日目: Deep Readonly
問題 & 解答
オブジェクトの各プロパティを再帰的にreadonlyにするDeepReadonly<T>を実装する。
前提:オブジェクトのみが渡される前提(クラスや配列、関数は考慮しなくてよい)
思考過程
単純にReadonlyにするのであれば、Mapped Typesで定義すれば良い。
type Readonly<T> = { readonly [P in keyof T]: T[P] }再帰的にこの定義を実装していくので、Recursive Conditional Typesも使う。
type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends T[P]がオブジェクト ? DeepReadonly<T[P]> : T[P] }T[P]がオブジェクトであるかどうかってどうやって判断するんだっけ?
ここでいうオブジェクトには関数とか配列は含まなくて、単純な{ key: value }構造
インデックスシグネチャを利用する?ブル本には推奨されていなかったけど、一旦これでやってみる。
type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends { [key: string]: unknown } ? DeepReadonly<T[P]> : T[P] }一応代入しようとすると、readonlyになっているんだけど、型定義を確認すると
type Expected = { readonly a: () => 22 readonly b: string // DeepReadonlyは適用されているものの、展開されていない readonly c: <DeepReadonly { d: boolean // 略となっていて、厳密な一致にはなっていない模様。
…というところでもうわからんとなったので、他の方の回答をみました。
type DeepReadonly<T> = keyof T extends never ? T : { readonly [k in keyof T]: DeepReadonly<T[k]> }https://github.com/type-challenges/type-challenges/issues/187
base caseをkeyof T extends neverで拾って、recursive caseでDeepReadonly<T>を呼び出しています。
感想
Objectの中でRecursive Conditional Typesを使うとなぜだめなのかがわかりませんでした。
ちゃんと代入しようとするとreadonlyになっているのに…。