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
1. Sintaxis básica
Section titled “1. Sintaxis básica”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 coincidePlaceholders con tipos específicos
Section titled “Placeholders con tipos específicos”type RutaUsuario = `/usuario/${number}`;
const r1: RutaUsuario = "/usuario/42"; // ✅const r2: RutaUsuario = "/usuario/abc"; // ❌ number esperadoconst r3: RutaUsuario = "/usuario/"; // ❌2. Uniones en template literals
Section titled “2. Uniones en template literals”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.
Múltiples uniones simultáneas
Section titled “Múltiples uniones simultáneas”type Prefijo = "get" | "set" | "delete";type Recurso = "user" | "post" | "comment";
type MetodosAPI = `${Prefijo}${Capitalize<Recurso>}`;// "getUser" | "getPost" | "getComment"// | "setUser" | "setPost" | "setComment"// | "deleteUser" | "deletePost" | "deleteComment"3. Utility types para strings
Section titled “3. Utility types para strings”TypeScript 4.1+ incluye tipos intrínsecos para manipulación de strings:
| Tipo | Descripció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"Aplicación: naming conventions
Section titled “Aplicación: naming conventions”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"4. Inferencia con template literals
Section titled “4. Inferencia con template literals”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">; // neverParsear rutas complejas
Section titled “Parsear rutas complejas”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" }5. Aplicaciones prácticas
Section titled “5. Aplicaciones prácticas”Sistema de eventos tipados
Section titled “Sistema de eventos tipados”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", () => {}); // ❌ ErrorCSS Properties tipadas
Section titled “CSS Properties tipadas”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};Mapped types + template literals
Section titled “Mapped types + template literals”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 }Strings con formato específico
Section titled “Strings con formato específico”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"; // ❌6. Advertencias ⚠️
Section titled “6. Advertencias ⚠️”// Las uniones grandes pueden afectar el rendimientotype Todos = `${string}-${string}-${string}`; // ⚠️ Prácticamente infinito
// Los template literals con string puro no acotan realmentetype 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.