Concepto 9 de 12
Tipos de literal de plantilla
Los tipos de literal de plantilla permiten al compilador construir y emparejar cadenas. Combinados con infer y recursión pueden analizar — así una ruta se convierte en un objeto de parámetros, y así se comprueban en compilación las claves i18n y los nombres de columna de un ORM.
- Expresión
EventName<"name"> - Se resuelve a
"nameChanged" - Expresión
EventName<"name" | "age"> - Se resuelve a
"nameChanged" | "ageChanged"
Una unión en una plantilla produce un literal de cadena por miembro. Ocurre en el sistema de tipos, sin ejecutar JavaScript.
Definición del tipo
type EventName<T extends string> = `${T}Changed`Construir y convertir a cadena
type Greeting = `hello ${string}` type Event = `on${'Click' | 'Focus'}` // 'onClick' | 'onFocus' type AsText = `${42}` // '42' // Interpolating a union multiplies out every combination. type Cell = `${'a' | 'b'}${1 | 2}` // 'a1' | 'a2' | 'b1' | 'b2'
Interpolar una unión produce el producto cartesiano, que crece rápido — TypeScript limita el resultado a 100.000 miembros y da error por encima. Convertir un número con ${N} es la forma estándar de transformar un problema aritmético en uno de texto.
El emparejamiento está anclado y es ávido por la izquierda
type StartsWith<S, P extends string> = S extends `${P}${string}` ? true : false type EndsWith<S, P extends string> = S extends `${string}${P}` ? true : false type Contains<S, P extends string> = S extends `${string}${P}${string}` ? true : false // Greedy from the left: Head binds the SHORTEST prefix that works. type Split1<S> = S extends `${infer Head}-${infer Tail}` ? [Head, Tail] : never type X = Split1<'a-b-c'> // ['a', 'b-c']
Un patrón debe emparejar toda la cadena, así que donde pongas ${string} es donde permites holgura. Holgura delante significa «contiene», detrás «empieza por», y a ambos lados significa que has escrito sin querer un test de subcadena.
Los cuatro tipos intrínsecos de mayúsculas
Uppercase<'abc'> // 'ABC' Lowercase<'ABC'> // 'abc' Capitalize<'abc'> // 'Abc' Uncapitalize<'Abc'> // 'abc'
Estos cuatro están implementados de forma nativa en el compilador y no tienen código TypeScript. Recurre a ellos antes de escribir una recursión carácter a carácter — CamelCase y compañía son unas pocas líneas con ellos y una pesadilla sin ellos.
El error más común
type Replace<S extends string, From extends string, To extends string> = S extends `${infer H}${From}${infer T}` ? `${H}${To}${T}` : S Replace<'abc', '', 'x'> // 'xabc' — not 'abc'
La cadena vacía cabe entre dos caracteres cualesquiera, así que una aguja vacía encaja de inmediato con H vacío. Es el null de los tipos de literal de plantilla: encaja en todas partes y rompe en silencio los patrones que asumen que encajar significa haber encontrado algo.
Ejercicio
Construye un nombre de manejador: click se convierte en onClick.
Pruébalo
2Handler<'click'>—→ 'onClick'Handler<'focus' | 'blur'>—→ 'onFocus' | 'onBlur'
Para recordar
Los patrones están anclados por ambos extremos y son ávidos por la izquierda. Dónde permites holgura decide qué estás preguntando de verdad, y la cadena vacía siempre es el caso límite.

