Kodokon kodokon.com

Template literal types: cadenas calculadas en tiempo de compilación

Aprovecha los template literal types para generar uniones mediante productos cartesianos y parsear literales de cadena con infer.

9 min · 3 preguntas

Abrir esta lección en Kodokon

Los template literal types llevan la sintaxis de los literales de plantilla al mundo de los tipos. Interpola una unión y el compilador calcula el producto cartesiano: cada combinación posible se convierte en un miembro del resultado. Esta es la herramienta que permite tipar claves de eventos, rutas o tokens de un design system hasta el carácter exacto.

TYPESCRIPT
type Lang = "fr" | "en";
type Theme = "light" | "dark";

type ThemeKey = `${Lang}-${Theme}`;
// "fr-light" | "fr-dark" | "en-light" | "en-dark"
Dos uniones interpoladas: un producto cartesiano de 4 miembros.

El verdadero poder aparece cuando se combina con infer: un template literal type se convierte en un patrón de coincidencia sobre cadenas. El compilador corta el literal de entrada para hacer coincidir cada segmento; cuando son posibles varios cortes, gana la primera aparición del separador. Añade recursión y obtienes auténticos parsers evaluados en tiempo de compilación.

TYPESCRIPT
type Split<S extends string, Sep extends string> =
  S extends `${infer Head}${Sep}${infer Rest}`
    ? [Head, ...Split<Rest, Sep>]
    : [S];

type Parts = Split<"a.b.c", ".">;
// ["a", "b", "c"]
Un split recursivo completamente estático.

Este patrón brilla al extraer los parámetros de una ruta estilo Express. Cada llamada recursiva consume un segmento :param y la unión final lista cada nombre de parámetro, que luego puedes alimentar a un Record para tipar la firma del handler.

TYPESCRIPT
type Params<Path extends string> =
  Path extends `${string}:${infer P}/${infer Rest}`
    ? P | Params<Rest>
    : Path extends `${string}:${infer P}`
      ? P
      : never;

type RouteParams = Params<"/users/:id/posts/:postId">;
// "id" | "postId"
Extraer los parámetros de ruta en tiempo de compilación.

TypeScript incluye cuatro utilidades intrínsecas - Uppercase, Lowercase, Capitalize, Uncapitalize - implementadas directamente dentro del compilador (la palabra clave intrinsic), de modo que no puedes reescribirlas tú mismo. Otro patrón de producción: string & {}. En una unión, string absorbería los literales y el editor perdería el autocompletado; intersecar con {} crea un tipo equivalente pero distinto que el compilador no reduce.

TYPESCRIPT
type Method = "get" | "post";
type Handler = `on${Capitalize<Method>}`;
// "onGet" | "onPost"

type KnownColor = "red" | "green" | "blue";
type Color = KnownColor | (string & {});

declare function paint(color: Color): void;

paint("red");     // suggested by autocompletion
paint("#ff8800"); // also accepted
Intrínsecas y el truco string & {} para el autocompletado.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. Si A tiene 3 miembros y B tiene 4, ¿cuántos miembros contiene ${A}-${B}?
    • 7
    • 12
    • 1
    • 4
  2. ¿Qué produce Capitalize cuando se aplica al literal "hello world"?
    • "Hello World"
    • "Hello world"
    • "HELLO WORLD"
  3. ¿Por qué escribir KnownColor | (string & {}) en lugar de KnownColor | string?
    • Para prohibir cadenas fuera de KnownColor
    • Para evitar que string absorba los literales y conservar el autocompletado
    • Para mejorar el rendimiento del compilador
    • Las dos formas son estrictamente equivalentes