Concepto 10 de 12
Recursión
No hay bucles a nivel de tipos, así que la recursión es la única forma de procesar algo de longitud desconocida. La forma es siempre la misma: separa una pieza, trátala, recurre sobre el resto y para cuando no quede nada.
- Expresión
Unwrap<Promise<Promise<string>>> - Se resuelve a
string - Expresión
Unwrap<Promise<string>> - Se resuelve a
string - Expresión
Unwrap<string> - Se resuelve a
string
Cada llamada recursiva elimina una capa de Promise. El string final es el caso base: devuelve T y termina.
Definición del tipo
type Unwrap<T> = T extends Promise<infer U> ? Unwrap<U> : TCabeza y cola
type Reverse<T extends readonly unknown[]> = T extends readonly [infer H, ...infer R] ? [...Reverse<R>, H] : [] // The order you REBUILD in is the whole difference: // [H, ...Reverse<R>] copies // [...Reverse<R>, H] reverses
La rama falsa es la salida y debe devolver el elemento neutro de lo que construyes: [] para tuplas, '' para cadenas, {} para objetos. Devuelve ahí el contenido y tendrás un desfase de uno que solo se ve al final.
Los acumuladores construyen hacia delante
type Unique<T extends readonly unknown[], Acc extends unknown[] = []> = T extends readonly [infer H, ...infer R] ? Includes<Acc, H> extends true ? Unique<R, Acc> : Unique<R, [...Acc, H]> : Acc
Un parámetro con valor por defecto lleva estado que quien llama nunca pasa. Más allá de la corrección — es lo que permite a Unique conservar la *primera* aparición y no la última — la forma de recursión de cola también gasta menos pila del compilador que construir el resultado al salir.
Hay un límite de profundidad
// Around 1000 levels, then: // "Type instantiation is excessively deep and possibly infinite."
TypeScript se detiene sobre las mil instanciaciones. La recursión de cola sube bastante el techo, pero el límite es real y por eso nadie publica una calculadora a nivel de tipos que maneje números grandes.
El error más común
// Intent: flatten completely, to any depth. type Flatten<T extends readonly unknown[]> = T extends readonly [infer H, ...infer R] ? H extends readonly unknown[] ? [...H, ...Flatten<R>] // <- only unwraps ONE layer : [H, ...Flatten<R>] : [] Flatten<[[[1]]]> // [[1]] — stopped one level early
Expandir H abre una capa; recurrir dentro de ella las abre todas. Recurrir en una dirección recorre una lista, en dos recorre un árbol — y «solo bajó un nivel» es casi siempre una segunda llamada recursiva que falta.
Ejercicio
Desenvuelve una promesa sin importar cuántas capas tenga.
Pruébalo
2Unwrap<Promise<string>>—→ stringUnwrap<Promise<Promise<number>>>—→ number
Para recordar
Divide, trata, recurre, para. El caso base devuelve el elemento neutro, y la dirección en que reensamblas es lo que distingue una copia de una inversión, un filtro o un map.
Se practica en
- #215Solo lectura profundoPar 4
- #222InviertePar 4
- #227IncluyePar 3
- #229AplanaPar 4
- #237ÚnicosPar 4
- #240FiltraPar 3
- #245RutasPar 5
- #251Opcional hasta el fondoPar 4
- #259Obtener por rutaPar 5
- #260Cuánto midePar 4
- #265Cuál es mayorPar 4
- #266Comprime un objetoPar 5
- #271Quita un carácterPar 3
- #272Tupla a objeto anidadoPar 4
- #279Mutable hasta el fondoPar 4

