Rozwiązanie z komentarzem
Opublikowano 24 lipca 2026Rozwiązanie
type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T
Najczęstsza błędna odpowiedź
type DeepReadonly<T> = T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T
Bez osłony na funkcje `() => void` pasuje do `extends object` i zostaje zmapowane. Typ funkcyjny nie ma własnych właściwości wyliczalnych, więc wynikiem jest `{}` — sygnatura wywołania przepada.
Linia po linii
T extends (...args: never[]) => unknown`never[]` w pozycji parametru akceptuje dowolną sygnaturę, bo parametry sprawdzane są kontrawariantnie. To bezpieczny sposób na powiedzenie „dowolna funkcja” bez użycia `any`.
{ readonly [K in keyof T]: DeepReadonly<T[K]> }Modyfikator `readonly` dodawany jest podczas mapowania, a typ wartości wchodzi w rekurencję. Jeden typ mapowany załatwia obie sprawy.
Do zapamiętania
Gałęzie warunku sprawdzane są po kolei, więc węższe przypadki idą pierwsze. `extends object` jest szersze, niż się większości wydaje — tablice i funkcje też je spełniają.