Solución comentada
Publicado el 2 de agosto de 2026La solución
type Join<T extends readonly string[], D extends string> = T extends readonly [infer F extends string, ...infer R extends string[]] ? R extends readonly [] ? F : `${F}${D}${Join<R, D>}` : ''
El error más común
type Join<T extends readonly string[], D extends string> = T extends readonly [infer F extends string, ...infer R extends string[]] ? `${F}${D}${Join<R, D>}` : ''
Cada nivel añade un delimitador, también el último, así que `["a","b","c"]` da `"a-b-c-"`. Unir no es «poner un separador tras cada elemento», sino «poner un separador *entre* elementos», y la diferencia solo se ve al final.
Línea por línea
infer F extends stringLa restricción `extends string` sobre un `infer` acota lo que se liga. Sin ella `F` sería `unknown`, que no puede interpolarse en un tipo de literal de plantilla.
R extends readonly [] ? FLa guarda que lo convierte en una unión y no en un sufijo. Cuando no sigue nada, el elemento se emite desnudo y la recursión termina sin aportar delimitador.
Para recordar
La construcción recursiva de cadenas casi siempre necesita un caso base distinto para el último elemento. Si tu salida acaba con un delimitador suelto, ese caso falta.