Solución comentada
Publicado el 24 de julio de 2026La solución
type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T
El error más común
type DeepReadonly<T> = T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T
Sin la guarda para funciones, `() => void` encaja en `extends object` y se mapea. Un tipo función no tiene propiedades enumerables propias, así que el resultado es `{}` — se pierde la firma invocable.
Línea por línea
T extends (...args: never[]) => unknown`never[]` en posición de parámetro acepta cualquier firma porque los parámetros se comprueban de forma contravariante. Es la manera segura de decir «cualquier función» sin recurrir a `any`.
{ readonly [K in keyof T]: DeepReadonly<T[K]> }El modificador `readonly` se añade durante el mapeo y el tipo del valor recurre. Un solo tipo mapeado hace ambas cosas.
Para recordar
Las ramas condicionales se prueban en orden, así que los casos más estrechos van primero. `extends object` es más amplio de lo que casi todo el mundo espera — arrays y funciones lo cumplen.