
El problema que resuelve satisfies
Antes de TypeScript 4.9, cuando querías asegurarte de que un objeto cumplía con un tipo determinado tenías básicamente dos opciones: anotar la variable con el tipo o usar as. Ambas tienen inconvenientes.
Con la anotación de tipo directa, TypeScript amplía el tipo de la variable al tipo declarado, perdiendo la información más específica que puede inferir:
type Color = "rojo" | "verde" | "azul";
type Paleta = Record<string, Color | number[]>;
// Con anotación directa: pierde información específica
const paleta1: Paleta = {
rojo: [255, 0, 0],
verde: "verde",
azul: [0, 0, 255]
};
// TypeScript solo sabe que paleta1.rojo es Color | number[]
// Por eso esto falla:
paleta1.rojo.map(x => x * 2); // Error: Property 'map' does not exist on type 'Color | number[]'
Con as evitas el error pero desactivas la comprobación del compilador, lo que puede ocultar errores reales.
La solución: el operador satisfies
El operador satisfies (introducido en TypeScript 4.9) válida que una expresión cumple con un tipo sin ampliar el tipo inferido. Dicho de otra forma: válida sin borrar.
type Color = "rojo" | "verde" | "azul";
type Paleta = Record<string, Color | number[]>;
// Con satisfies: TypeScript valida Y conserva los tipos específicos
const paleta = {
rojo: [255, 0, 0],
verde: "verde",
azul: [0, 0, 255]
} satisfies Paleta;
// Ahora TypeScript sabe que paleta.rojo es number[], no Color | number[]
paleta.rojo.map(x => x * 2); // ✓ Sin error
paleta.verde.toUpperCase(); // ✓ Sin error, es string
paleta.rojo.toUpperCase(); // ✗ Error, es number[], no string
Validación sin pérdida de tipo
El principal beneficio de satisfies es exactamente ese: la validación ocurre en tiempo de compilación, igual que con una anotación de tipo, pero el tipo inferido que usa el compilador para el resto del código sigue siendo el más específico.
type ConfigBase = {
host: string;
puerto: number;
tls: boolean;
};
const config = {
host: "localhost",
puerto: 5432,
tls: false,
nombre: "mi-base-de-datos" // ← Esta propiedad extra provocaría error con anotación directa
} satisfies ConfigBase; // ✗ Error: Object literal may only specify known properties
En este caso satisfies se comporta igual que la anotación directa para la validación, pero si el objeto fuera exactamente conforme, config.host seguiría siendo "localhost" (tipo literal) y no string.
Uso con tipos unión
satisfies resulta especialmente útil con tipos unión donde cada propiedad puede ser de tipos distintos:
type ValorConfiguracion = string | number | boolean | string[];
type Configuracion = Record<string, ValorConfiguracion>;
const config = {
apiUrl: "https://api.ejemplo.com",
maxReintentos: 3,
debug: true,
origenesPermitidos: ["https://app.ejemplo.com", "https://admin.ejemplo.com"]
} satisfies Configuracion;
// TypeScript mantiene los tipos específicos:
config.apiUrl.startsWith("https"); // ✓ Es string
config.maxReintentos.toFixed(2); // ✓ Es number
config.debug.valueOf(); // ✓ Es boolean
config.origenesPermitidos.join(", "); // ✓ Es string[]
Comparación: satisfies vs anotación directa vs as const
Cada herramienta tiene su papel en TypeScript moderno:
type Rol = "admin" | "editor" | "lector";
type ConfigUsuario = { rol: Rol; activo: boolean };
// Anotación directa: el tipo de la variable ES ConfigUsuario
const u1: ConfigUsuario = { rol: "admin", activo: true };
// u1.rol es Rol (string amplio), no "admin"
// as const: inmutalidad máxima, todos los valores son literales
const u2 = { rol: "admin", activo: true } as const;
// u2.rol es "admin" (literal), u2.activo es true (literal)
// satisfies: valida que cumple ConfigUsuario, pero conserva inferencia específica
const u3 = { rol: "admin", activo: true } satisfies ConfigUsuario;
// u3.rol es "admin" (literal) pero TypeScript ha validado que es Rol válido
// u3.activo es boolean (no true literal, porque ConfigUsuario lo define como boolean)
Combinando satisfies con as const
Puedes combinar ambos operadores cuando necesitas lo máximo de los dos mundos: validación del tipo Y tipos más literales posibles.
type Tema = "claro" | "oscuro" | "sistema";
type ConfigTema = { tema: Tema; radio: number; fuente: string };
const configTema = {
tema: "oscuro",
radio: 8,
fuente: "Inter"
} as const satisfies ConfigTema;
// configTema.tema es "oscuro" (literal), no Tema
// configTema.radio es 8 (literal), no number
// configTema.fuente es "Inter" (literal), no string
// Y TypeScript ha validado que cumple con ConfigTema
El orden importa: primero as const, luego satisfies. De esta forma el compilador aplica primero la inferencia literal y luego válida el cumplimiento del tipo.
Casos de uso reales
Configuraciones de aplicación
type OpcionesVite = {
puerto: number;
host: string;
open: boolean;
};
export const configServidor = {
puerto: 5173,
host: "0.0.0.0",
open: false
} satisfies OpcionesVite;
Mapas de constantes tipados
type MensajeError = { codigo: number; mensaje: string };
type MapaErrores = Record<string, MensajeError>;
export const ERRORES = {
NO_ENCONTRADO: { codigo: 404, mensaje: "Recurso no encontrado" },
NO_AUTORIZADO: { codigo: 401, mensaje: "Sin autorización" },
SERVIDOR: { codigo: 500, mensaje: "Error interno del servidor" }
} satisfies MapaErrores;
// ERRORES.NO_ENCONTRADO.codigo es number (de MapaErrores)
// Pero TypeScript sabe que ERRORES.NO_ENCONTRADO existe como propiedad
Diccionarios de rutas
type Ruta = { path: string; title: string; autenticada: boolean };
type MapaRutas = Record<string, Ruta>;
export const RUTAS = {
INICIO: { path: "/", title: "Inicio", autenticada: false },
PERFIL: { path: "/perfil", title: "Mi perfil", autenticada: true },
AJUSTES: { path: "/ajustes", title: "Ajustes", autenticada: true }
} satisfies MapaRutas;
Cuándo usar satisfies vs anotación directa
Usa anotación directa (:) cuando:
- Quieres que la variable tenga exactamente ese tipo en el scope actual
- Necesitas asignabilidad posterior a otras variables del mismo tipo
- El tipo más genérico es el que necesitas
Usa satisfies cuando:
- Necesitas validar que el objeto cumple un contrato
- Quieres mantener el tipo inferido más específico para el autocompletado
- Estás construyendo objetos de configuración o constantes tipadas
- Combinas con
as constpara máxima especificidad
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en TypeScript
Documentación oficial de TypeScript
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, TypeScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de TypeScript
Explora más contenido relacionado con TypeScript y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender el problema que resuelve satisfies frente a las anotaciones de tipo directas
- Aplicar satisfies para validar objetos de configuración manteniendo tipos específicos
- Distinguir cuándo usar satisfies, as, los dos puntos (:) o una combinación
- Usar satisfies con tipos genéricos y tipos unión para mayor flexibilidad
- Identificar casos de uso reales donde satisfies mejora la seguridad y el autocompletado