Skip to content

Lección 94: Template Literal Types

🎯 Objetivo: Dominar los template literal types para crear tipos de strings dinámicos, combinarlos con uniones y usar inferencia para extraer información.


🧠 Concepto

Los template literal types usan la misma sintaxis que los template literals de JavaScript, pero en el sistema de tipos.

💻 Ejemplo

type Saludo = `Hola, ${string}!`;
const msg1: Saludo = "Hola, mundo!"; // ✅
const msg2: Saludo = "Hola, TypeScript!"; // ✅
const msg3: Saludo = "Hello!"; // ❌ No coincide
type RutaUsuario = `/usuario/${number}`;
const r1: RutaUsuario = "/usuario/42"; // ✅
const r2: RutaUsuario = "/usuario/abc"; // ❌ number esperado
const r3: RutaUsuario = "/usuario/"; // ❌

Cuando usas una unión en un placeholder, el template literal genera todas las combinaciones.

type Size = "small" | "medium" | "large";
type Color = "red" | "green" | "blue";
type ClaseCSS = `${Size}-${Color}`;
// "small-red" | "small-green" | "small-blue"
// | "medium-red" | "medium-green" | "medium-blue"
// | "large-red" | "large-green" | "large-blue"

🧠 Multiplicación de tipos: Si tienes 3 sizes × 3 colores = 9 combinaciones. Con más uniones, crece exponencialmente. Útil para generar todas las variantes posibles.

type Prefijo = "get" | "set" | "delete";
type Recurso = "user" | "post" | "comment";
type MetodosAPI = `${Prefijo}${Capitalize<Recurso>}`;
// "getUser" | "getPost" | "getComment"
// | "setUser" | "setPost" | "setComment"
// | "deleteUser" | "deletePost" | "deleteComment"

TypeScript 4.1+ incluye tipos intrínsecos para manipulación de strings:

TipoDescripción
Uppercase<T>Convierte a MAYÚSCULAS
Lowercase<T>Convierte a minúsculas
Capitalize<T>Primera letra en mayúscula
Uncapitalize<T>Primera letra en minúscula
type Upper = Uppercase<"hola">; // "HOLA"
type Lower = Lowercase<"HOLA">; // "hola"
type Cap = Capitalize<"hola mundo">; // "Hola mundo"
type Uncap = Uncapitalize<"Hola">; // "hola"
type CamelToSnake<S extends string> =
S extends `${infer First}${infer Rest}`
? First extends Uppercase<First>
? `_${Lowercase<First>}${CamelToSnake<Rest>}`
: `${First}${CamelToSnake<Rest>}`
: S;
type EjemploSanke = CamelToSnake<"miPropiedadFavorita">;
// "mi_propiedad_favorita"

Puedes usar infer dentro de template literals para extraer partes de un string.

type ExtraerId<Ruta extends string> =
Ruta extends `/usuario/${infer Id}` ? Id : never;
type Id1 = ExtraerId<"/usuario/42">; // "42"
type Id2 = ExtraerId<"/usuario/abc">; // "abc"
type Id3 = ExtraerId<"/post/123">; // never
type RutaAPI = "/api/v1/usuarios/42/posts/5";
type ParsearRuta<R extends string> =
R extends `/api/v1/${infer Recurso}/${infer Id}/${infer Subrecurso}/${infer SubId}`
? { recurso: Recurso; id: Id; subrecurso: Subrecurso; subId: SubId }
: never;
type Resultado = ParsearRuta<RutaAPI>;
// { recurso: "usuarios"; id: "42"; subrecurso: "posts"; subId: "5" }

type Componente = "button" | "input" | "form";
type Accion = "click" | "change" | "focus" | "blur";
type EventoUI = `${Componente}:${Accion}`;
// "button:click" | "button:change" | ... | "form:blur"
function escucharEvento(evento: EventoUI, handler: () => void) {
// ...
}
escucharEvento("button:click", () => {}); // ✅
escucharEvento("input:focus", () => {}); // ✅
escucharEvento("div:click", () => {}); // ❌ Error
type PropiedadCSS = "margin" | "padding" | "border";
type Lado = "top" | "right" | "bottom" | "left";
type PropiedadCompleta = PropiedadCSS | `${PropiedadCSS}-${Lado}`;
// "margin" | "margin-top" | "margin-right" | ... | "border-left"
type ValorCSS = string | number;
type Estilos = Partial<Record<PropiedadCompleta, ValorCSS>>;
const estilos: Estilos = {
margin: "10px",
"padding-top": "5px", // ✅
"border-left": "1px solid black", // ✅
"background": "red", // ❌ Error
};
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
type Persona = { nombre: string; edad: number };
type GetPersona = Getters<Persona>;
// { getNombre: () => string; getEdad: () => number }
type HexColor = `#${string}`;
const color1: HexColor = "#ff0000"; // ✅
const color2: HexColor = "#abc123"; // ✅
const color3: HexColor = "ff0000"; // ❌ falta #
type Version = `${number}.${number}.${number}`;
const v1: Version = "1.0.0"; // ✅
const v2: Version = "2.4.12"; // ✅
const v3: Version = "1.0"; // ❌

// Las uniones grandes pueden afectar el rendimiento
type Todos = `${string}-${string}-${string}`; // ⚠️ Prácticamente infinito
// Los template literals con string puro no acotan realmente
type CualquierString = `prefix-${string}`; // Sigue siendo string esencialmente

📝 Resumen: Los template literal types permiten modelar patrones de strings en el sistema de tipos. Combinados con uniones generan todas las combinaciones posibles, y con infer puedes parsear y extraer información de strings. Ideales para rutas, eventos, CSS y naming conventions.