Solución comentada
Publicado el 21 de agosto de 2026La solución
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
El error más común
type Unique<T extends readonly unknown[]> = T extends readonly [infer H, ...infer R] ? Includes<R, H> extends true ? Unique<R> : [H, ...Unique<R>] : []
Esto mira hacia delante en vez de recordar. Cuando un elemento reaparece más tarde, descarta la *primera* aparición y conserva la última, así que `[1, 2, 2, 3, 1]` queda como `[2, 3, 1]`. Los elementos son correctos, el orden no.
Línea por línea
Acc extends unknown[] = []Un parámetro de tipo con valor por defecto es cómo una función a nivel de tipos lleva estado. Quien la llama nunca lo pasa; cada llamada recursiva encadena el resultado que va creciendo.
Includes<Acc, H>La prueba de pertenencia se hace contra lo ya conservado, no contra lo que queda por venir. Esa es la diferencia entre conservar la primera aparición y conservar la última.
: AccCuando la entrada se agota, el acumulador *es* la respuesta, así que se devuelve directamente en vez de construirlo al salir. Es una forma de recursión de cola, y TypeScript la maneja con menos pila que la forma cabeza-cola.
Para recordar
Un parámetro acumulador convierte una recursión que construye hacia atrás en una que construye hacia delante. Cuesta un parámetro de tipo y aporta orden correcto y un límite de recursión más profundo.
