Saltar al contenido
webtype.orgwebtype.orgDoce conceptos · 10/12

    ↑↓ mover · ⏎ abrir · esc cerrar

    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.

    Cabeza 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

    2
    • Unwrap<Promise<string>>
      string
    • Unwrap<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