Mejora la inferencia de tipos en TypeScript con el operador satisfies
satisfies operator: el operador de TypeScript que no estás usando
Tiempo estimado de lectura: 5 min
- Valida sin perder inferencia: El operador
satisfiesvalida que una expresión cumpla un tipo sin cambiar el tipo inferido del valor. - Mejora la DX en repos grandes: Conserva literales y autocompletado donde las anotaciones tradicionales causan upcast o las aserciones apagan la seguridad.
- Casos de uso claros: Constants, routes, design tokens y mapeos estáticos se benefician más.
- Complemento con as const: Úsalo junto a
as constpara validación más inmutabilidad absoluta. - No es para runtime: No reemplaza validación en tiempo de ejecución para datos dinámicos.
Introducción
satisfies operator: el operador de TypeScript que no estás usando es la frase que deberías leer en todos los PRs donde alguien fuerza tipos con as o sacrifica la inferencia de literales. Introducido en TypeScript 4.9, satisfies arregla —sin drama— un problema de tipado que hemos parcheado mal durante años.
Resumen rápido (lectores con prisa)
Qué es: Un operador de TypeScript que valida que un valor cumple un tipo sin cambiar la inferencia del valor.
Cuándo usarlo: Para constantes exportadas, rutas, tokens de diseño y mapeos estáticos donde quieres conservar literales.
Por qué importa: Mantiene autocompletado y seguridad de tipos en grandes bases de código.
Cómo funciona: Verifica el contrato en tiempo de compilación y deja intacta la inferencia literal.
¿Qué hace exactamente el satisfies operator y por qué importa?
El operador satisfies valida que una expresión cumpla con un tipo, pero no cambia el tipo inferido del valor. Es decir: verifica el contrato y deja intacta la inferencia literal del valor. Eso suena pequeño; en equipos con bases de código grandes es una diferencia estructural.
Comparación con anotación y aserción
- La anotación
const x: T = ...valida pero hace upcast: pierde literales. - La aserción
const x = ... as Tfuerza sin validar: apaga la seguridad. const x = ... satisfies Tvalida y conserva la inferencia.
Ejemplo práctico: tema de colores que no deberías arruinar
Sin satisfies, acabas escribiendo código que obliga al IDE a perder información útil:
type Color = string | [number, number, number];
const theme: Record = {
primary: "blue",
secondary: [255, 0, 0]
};
theme.primary.startsWith("b"); // Error: theme.primary es Color, no string literal
Con satisfies:
const theme = {
primary: "blue",
secondary: [255, 0, 0]
} satisfies Record;
theme.primary.startsWith("b"); // OK — TypeScript sabe que es string
theme.secondary[0]; // OK — sabe que es number
Validación sin amputación de tipos. Eso es todo.
Casos de uso donde satisfies aporta valor real
– Configuraciones públicas (constants.ts). Valores exportados y consumidos desde varios módulos se benefician de mantener literales.
– Rutas y diccionarios (routing). keyof typeof ROUTES debe devolver claves concretas, no string.
– Design tokens y paletas. Necesitas diferenciar hex strings de tuplas RGB sin perder método de string/array.
– API clients estáticos o mapeos entre endpoints y tipos de respuesta.
Ejemplo de rutas
type RouteConfig = Record;
const ROUTES = {
home: { path: "/", protected: false },
dashboard: { path: "/app", protected: true },
} satisfies RouteConfig;
// keyof typeof ROUTES => "home" | "dashboard"
Si hubieras usado : RouteConfig, perderías esas claves y con ellas, seguridad y autocompletado.
Patrón avanzado: satisfies + as const
Úsalos juntos cuando quieras validación + inmutabilidad absoluta:
const ENDPOINTS = {
users: "/api/users",
session: "/api/session",
} satisfies Record as const;
// ENDPOINTS.users es literal "/api/users" y readonly
Esto es ideal para inyectar constantes que se comparten por toda la app sin riesgo de mutación accidental.
Cuándo no usar satisfies
No es una bala de plata. No lo apliques en cada tipo local ni donde el valor no necesite exportarse o conservar literales.
- Definición y consumo inmediato en el mismo scope → anotación clásica puede ser más clara.
- Datos dinámicos desde la red → valida en runtime (Zod, io-ts) y transforma antes de confiar en tipos.
Recuerda: satisfies es para diseño estático y claridad, no para validar payloads de clientes externos en producción.
Cómo adoptarlo en un repo sin romper nada
- Audit rápido: busca ficheros
constants,theme,routes,tokens. - Busca patrones problemáticos:
as constseguido deas Type, oconst X: Record<...> = {...}. - Reemplaza por
satisfiesdonde quieras conservar literales. - Añade tests de tipo (
tsd) para casos críticos y ejecutatsc --noEmiten CI. - Actualiza guía de estilo y explica el porqué en
CONTRIBUTING.md.
Comando grep útil:
grep -R --line-number -E "as const.*as |: Record<|: {[A-Za-z0-9_]+: .*}" src/
Impacto en equipo y mantenimiento
Adoptar satisfies reduce errores sutiles: llamadas a funciones con claves incorrectas, fallos de autocompletado que llevan a as any, y la necesidad de escribir casts defensivos. A nivel de DX, mejora el autocompletado y la intención del código. A nivel de arquitectura, reduce deuda técnica silenciosa: menos as T, menos // @ts-ignore.
Cierre práctico
No es una moda; es una herramienta ergonométrica del tipo system. Si tu repo exporta constantes que consumen otros módulos, haz una pasada hoy mismo y reemplaza las anotaciones que hacen upcast por satisfies. Empieza por constants.ts, theme.ts, routes.ts. Verás menos PRs con as any y más código que documenta intención y comportamiento real.
Implementarlo es simple. Ignorarlo es caro. Esto no acaba aquí: la próxima vez que veas un as en un PR, pregúntate si deberías usar satisfies en su lugar.
Referencia oficial (anuncio): la sección relevante y la documentación general.
FAQ
¿Qué hace exactamente satisfies?
¿Cuándo debo usarlo en lugar de una anotación de tipo?
¿Puede reemplazar validación en runtime?
¿Cómo se combina con as const?
... satisfies Record<string,string> as const mantiene literales y readonly.satisfies en mapeos permite que keyof typeof infiera claves concretas en lugar de string.¿Romperá código existente si lo introduzco en un repo grande?
constants, theme, routes, tokens y patrones como as const seguido de as Type o : Record.