Entrada / 1 mar. 2026
Template Literal Types en TypeScript: de strings a reglas
Por qué los template literal types permiten que TypeScript deje de tratar strings con patrón como texto suelto y empiece a modelarlos como reglas verificadas por el compilador.
Hay una etapa en TypeScript en la que el problema ya no es aprender a tipar lo básico.
Ya entiendes generics. Ya conoces utility types. Incluso es
posible que infer ya no te parezca raro.
Y justo ahí empieza a aparecer otro tipo de problema: strings que en realidad no son “solo strings”.
Tienen estructura.
Siguen convenciones de nombres, reglas del dominio y patrones repetidos que el equipo entiende de memoria, pero que el compilador todavía ve como texto plano.
Ese es exactamente el cambio de enfoque que plantea este video.
Los template literal types importan porque permiten que TypeScript modele esas reglas de texto directamente, en vez de obligarte a enumerar a mano todos los valores válidos.
El problema real es la estructura implícita
El video hace una observación importante desde el inicio: el problema no es que los strings sean malos.
El problema es que muchos sistemas esconden reglas dentro de strings sin enseñárselas al sistema de tipos.
Eso suele producir código en el que:
- los nombres válidos viven solo en la memoria del equipo
- los typos parecen strings normales hasta llegar a runtime
- agregar una nueva variante obliga a tocar varios lugares desconectados
- el compilador no puede ayudarte porque el contrato es demasiado amplio
Por eso los strings con patrón terminan siendo frágiles.
Parecen flexibles, pero en realidad suelen ser pequeños lenguajes no documentados.
Los template literal types convierten patrones en contratos
La idea central del video es simple y potente: describir la regla, no la lista final de valores.
Si un token de espaciado siempre se construye a partir de una propiedad y una dirección, esa relación se puede modelar de forma directa.
type Property = "padding" | "margin";
type Direction = "top" | "right" | "bottom" | "left";
type SpacingProperty = `${Property}-${Direction}`;
Con eso, TypeScript puede derivar:
padding-toppadding-rightmargin-bottommargin-left
Y también puede rechazar combinaciones inválidas como
"padding-center".
Ahí está la ganancia de diseño.
Ya no estás listando resultados.
Estás modelando cómo se construyen los resultados válidos.
Esto escala mejor que escribir uniones a mano
Claro, podrías escribir toda la unión manualmente.
Para un ejemplo pequeño, funciona.
Pero en cuanto el dominio crece, las uniones escritas a mano se convierten en el mismo problema de mantenimiento que las interfaces duplicadas.
Si agregas una nueva dirección como center, el tipo de
arriba se actualiza solo. Si agregas otra propiedad, todas
las combinaciones válidas aparecen sin volver a escribir la
matriz completa.
Eso es exactamente a lo que apunta el video cuando dice que el sistema empieza a entender el patrón.
Y cuando el compilador entiende el patrón, muchos cambios futuros dejan de depender de la memoria humana.
Los template literal types hacen el producto cartesiano de las uniones
Una idea útil para ampliar lo que muestra el video aparece de forma explícita en la documentación oficial de TypeScript: cuando interpolas uniones dentro de un template literal type, TypeScript genera todas las combinaciones posibles.
type Entity = "user" | "product" | "order";
type Event = "changed" | "deleted";
type EntityEvent =
`on${Capitalize<Entity>}${Capitalize<Event>}`;
Eso produce una unión como:
onUserChangedonUserDeletedonProductChangedonOrderDeleted
Ese comportamiento es justo lo que vuelve tan expresivos a los template literal types para convenciones de nombres, cache keys, ids de rutas, eventos, tokens tipo CSS y otros casos parecidos.
Los nombres de eventos muestran muy bien el valor práctico
El video pasa de tokens de espaciado a eventos del dominio, y ahí el beneficio se vuelve todavía más evidente.
Muchos proyectos terminan teniendo nombres como:
onUserChangedonProductDeletedonOrderChanged
Normalmente esos nombres viven como strings sueltos por todo el código.
Eso parece cómodo al principio, pero deja un contrato débil.
Con template literal types, puedes derivar esos nombres a partir de piezas reales del dominio.
type Entity = "user" | "product" | "order";
type EventType = "changed" | "deleted";
type DomainEvent =
`on${Capitalize<Entity>}${Capitalize<EventType>}`;
Ahora, si agregas invoice a Entity, TypeScript también
agrega automáticamente onInvoiceChanged y
onInvoiceDeleted.
Eso es más que autocompletado.
Es una mejor fuente de verdad.
Los helpers de strings vuelven estas reglas más expresivas
Los helpers integrados Uppercase, Lowercase,
Capitalize y Uncapitalize hacen que este patrón sea
mucho más útil en código real.
Permiten normalizar los strings generados sin tener que escribir a mano todas las reglas de casing.
type Entity = "user" | "product";
type Action = "created" | "deleted";
type AuditKey = `${Uppercase<Entity>}_${Uppercase<Action>}`;
// "USER_CREATED" | "USER_DELETED" | ...
Y eso importa porque muchos sistemas ya usan formatos de nombres para eventos analíticos, claves de configuración, identificadores de caché o topics de mensajería.
Los template literal types permiten que esos formatos pasen a formar parte del modelo de tipos en lugar de quedarse escondidos en convenciones informales.
El beneficio profundo es el pattern matching a nivel de tipos
El video también insinúa una idea todavía más interesante: una vez que TypeScript puede generar strings estructurados, también puede ayudarte a analizarlos.
Ahí es donde los template literal types se conectan muy bien
con generics, indexed access types e infer.
La documentación oficial muestra un ejemplo clásico con objetos observables:
type PropEventSource<Type> = {
on<Key extends string & keyof Type>(
eventName: `${Key}Changed`,
callback: (newValue: Type[Key]) => void,
): void;
};
Ese diseño hace dos cosas útiles al mismo tiempo:
- restringe el nombre del evento a un patrón válido
- infiere el payload del callback a partir de la propiedad correspondiente
Así, "ageChanged" te da un number, mientras que
"firstNameChanged" te da un string.
Eso ya no es simplemente “tipar strings”.
Es modelar relaciones.
Los mapped types hacen el patrón todavía más potente
Otra ampliación útil es combinar template literal types con mapped types y remapeo de claves.
type Getters<Type> = {
[Key in keyof Type as `get${Capitalize<string & Key>}`]: () => Type[Key];
};
Ahora, un objeto como este:
interface User {
name: string;
age: number;
}
Puede producir una API derivada como esta:
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }
Esa es la misma filosofía que defiende el video.
No mantengas a mano un montón de nombres relacionados si la regla de nombres se puede codificar una sola vez.
También hay un límite importante
Los template literal types son potentes, pero no son una excusa para construir parsers gigantes a nivel de tipos en cualquier caso.
En la práctica, hay dos límites importantes.
El primero es que las uniones muy grandes pueden crecer demasiado y volverse difíciles de leer o de mantener.
El segundo es que algunos strings son realmente demasiado dinámicos como para modelarlos con precisión en compilación.
Por eso la pregunta madura no es “¿puedo codificar esto?”
La pregunta correcta es “¿codificar esto mejora lo suficiente el contrato como para justificar la complejidad?”.
Cuando la respuesta es sí, los template literal types pueden
eliminar mucha fragilidad alrededor de strings. Cuando la
respuesta es no, un string normal junto con validación en
runtime puede ser la herramienta más honesta.
La ganancia de mantenibilidad es la verdadera historia
El video presenta los template literal types como una forma de modelar reglas, y ese es exactamente el marco correcto.
Su valor no está en que el código se vea más avanzado.
Su valor está en mover una convención desde la memoria humana hacia el compilador.
Una vez que la regla queda explícita, TypeScript puede:
- generar combinaciones válidas automáticamente
- rechazar combinaciones inválidas antes de runtime
- mantener nombres relacionados alineados cuando el dominio evoluciona
- volver más seguros los refactors porque una sola regla actualiza muchas salidas
Por eso esta característica importa tanto.
Convierte un límite frágil basado en texto en algo sobre lo que el sistema de tipos realmente puede razonar.
El takeaway
Los template literal types son útiles porque ayudan a que TypeScript trate texto estructurado como texto estructurado.
Te permiten pasar de:
- memorizar strings válidos
- copiar uniones largas a mano
- esperar que las convenciones se mantengan solas
A:
- codificar la regla una sola vez
- derivar strings válidos automáticamente
- dejar que el compilador proteja el patrón
Eso no es solo tipado ingenioso.
Es mejor diseño.
Y cuando tu dominio contiene strings que en realidad son reglas disfrazadas, los template literal types son una de las formas más claras de hacer visible ese diseño.
