Rarezas de TypeScript: lo que el compilador deja pasar

TypeScript atrapa la mayoría de los errores antes de ejecutar, pero no todos. A veces deja pasar código que falla, y a veces rechaza código que parece correcto. Estas son las rarezas de TypeScript más famosas, comprobadas con el compilador y ejecutadas de verdad, con la explicación de por qué pasan y la costumbre que las evita.

TypeScript promete atrapar los errores antes de ejecutar el programa, y casi siempre lo cumple. Pero tiene huecos, y no por descuido: muchos son decisiones conscientes de su equipo, que prefirió un lenguaje cómodo de usar con el JavaScript que ya existe antes que uno perfecto pero imposible de adoptar. Aquí están los más llamativos. Cada fragmento se comprobó con TypeScript 7.0.2 en modo estricto (tsc --strict, con los mensajes en español) y, cuando hace falta ver lo que pasa al ejecutarlo, con Node.js 24.

Todas las salidas de Node de esta página salen de ejecutar el archivo .ts directamente: Node 24 borra los tipos y ejecuta el resto sin revisarlos. Por eso un programa que compila «limpio» puede fallar, y uno con errores de tipo puede ejecutarse. Revisar los tipos es trabajo de tsc.

any lo contagia todo

const datos: any = JSON.parse('{"precio": "12.50"}');
const total: number = datos.precio + 1;
console.log(total);
console.log(total.toFixed(2));

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.ts12.501
y despuésTypeError: total.toFixed is not a function

Por qué: JSON.parse devuelve any, y any significa «no revises nada». Todo lo que se saca de un any también es any: datos.precio + 1 es any, y un any se puede asignar a una variable de tipo number sin ninguna queja. El precio venía como texto, así que la suma concatenó: total vale "12.501" aunque su tipo diga number, y toFixed no existe. Un solo any desactiva la revisión en todo el código por el que pasa.

Cómo evitarlo: trata lo que llega de fuera (JSON, formularios, APIs) como unknown y compruébalo antes de usarlo, a mano o con una biblioteca de validación como Zod. unknown acepta cualquier valor, pero no deja hacer nada con él hasta comprobar su tipo. Y vigila los any que no escribes tú: JSON.parse, response.json() o una biblioteca sin tipos.

as le da la razón a quien escribe

const usuario = JSON.parse('{"nombre": "Ana"}') as { nombre: string; edad: number };
console.log(usuario.nombre);
console.log(usuario.edad.toFixed(0));

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.tsAna
y despuésTypeError: Cannot read properties of undefined (reading 'toFixed')

Por qué: as no convierte nada ni comprueba nada al ejecutar: le dice al compilador «confía en mí, esto es de este tipo». El compilador confía, y si te equivocas, el error aparece donde menos lo esperas. Solo protesta cuando la afirmación es imposible, cuando los dos tipos no tienen nada que ver:

const a = "hola" as number;
const b = "hola" as unknown as number;

Ábrelo en el editor y cámbialo →

tsc --strictlínea 1: error TS2352: La conversión del tipo "string" al tipo "number" puede ser un error, porque ninguno de los tipos se superpone suficientemente al otro. Si esto era intencionado, convierta primero la expresión en "unknown".

Fíjate en que el propio mensaje te dice cómo saltarte la protección: con as unknown as number, la segunda línea compila sin rechistar. Es la puerta trasera del sistema de tipos.

Cómo evitarlo: usa as solo cuando sabes algo que el compilador no puede saber, y nunca para «callar» un error. Si el dato viene de fuera, valídalo; si es tuyo, casi siempre basta con anotar bien el tipo. Una doble conversión as unknown as en una revisión de código merece siempre una pregunta.

Las propiedades de más solo se vigilan a veces

interface Opciones {
  color?: string;
  ancho?: number;
}
function dibujar(o: Opciones) {
  console.log(o.color ?? "negro", o.ancho ?? 1);
}
dibujar({ colr: "rojo" });

Ábrelo en el editor y cámbialo →

tsc --strictlínea 8: error TS2561: El literal de objeto solo puede especificar propiedades conocidas, pero "colr" no existe en el tipo "Opciones". ¿Quería escribir "color"?

Bien: TypeScript detecta la errata y hasta sugiere la palabra correcta. Ahora, el mismo objeto guardado antes en una variable:

interface Opciones {
  color?: string;
  ancho?: number;
}
function dibujar(o: Opciones) {
  console.log(o.color ?? "negro", o.ancho ?? 1);
}
const misOpciones = { colr: "rojo", ancho: 2 };
dibujar(misOpciones);

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.tsnegro 2

Por qué: TypeScript es estructural: un valor sirve para un tipo si tiene lo que el tipo pide, aunque traiga más cosas. misOpciones tiene un ancho numérico, y como color es opcional, encaja en Opciones. La comprobación de «propiedades de más» es una regla extra que solo se aplica a los objetos escritos en el mismo sitio donde se usan, porque ahí una propiedad que sobra no puede servir para nada más y casi seguro es un error. El color pedido se pierde sin aviso: se dibuja en negro.

Si el objeto se escribe directamente en la llamada, TypeScript rechaza la propiedad desconocida colr; si llega en una variable, solo comprueba que estén las que pide y la errata pasaLa errata se detecta según cómo llega el objetoObjeto escrito en la llamadadibujar({ colr: "rojo" })Comprobación estricta:colr no existe: ERRORObjeto en una variabledibujar(misOpciones)Comprobación estructural:lo que sobra se ignoraUn literal recién escrito no puede ir a ningún otro sitio: si sobra algo, casi seguro es un error.

La misma errata, dos resultados: depende de si el objeto se escribe en la llamada o llega en una variable. Esquema: decodigo.com.

Hay una excepción que salva algunos casos: si la variable no tiene ninguna propiedad en común con un tipo cuyas propiedades son todas opcionales (un «tipo débil»), TypeScript sí protesta. Con const misOpciones = { colr: "rojo" }, sin el ancho:

tsc --strictlínea 9: error TS2559: El tipo "{ colr: string; }" no tiene propiedades en común con el tipo "Opciones".
Cómo evitarlo: escribe los objetos de opciones directamente en la llamada, o anota la variable al crearla: const misOpciones: Opciones = { colr: "rojo", ancho: 2 } sí da el error, porque el literal se comprueba contra el tipo en el momento de escribirlo.

Una unión de objetos acepta los dos a la vez

type Contacto = { correo: string } | { telefono: string };
const c: Contacto = { correo: "ana@ejemplo.com", telefono: "555-0100" };
console.log(c);

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.ts{ correo: 'ana@ejemplo.com', telefono: '555-0100' }

Por qué: una unión A | B significa «vale si encaja en A o en B», no «en uno y solo uno». El objeto encaja en { correo: string } (le sobra telefono, pero la otra rama de la unión la conoce, así que no cuenta como propiedad de más) y también en { telefono: string }. Si querías decir «o correo o teléfono, pero no los dos», este tipo no lo dice.

La solución es una unión discriminada: una propiedad común con un valor literal distinto en cada rama. Con ella, TypeScript sabe en qué rama estás y vuelve a vigilar las propiedades de más:

type Contacto =
  | { tipo: "correo"; correo: string }
  | { tipo: "telefono"; telefono: string };
const c: Contacto = { tipo: "correo", correo: "ana@ejemplo.com", telefono: "555-0100" };

Ábrelo en el editor y cámbialo →

tsc --strictlínea 4: error TS2353: El literal de objeto solo puede especificar propiedades conocidas y 'telefono' no existe en el tipo '{ tipo: "correo"; correo: string; }'.
Cómo evitarlo: cuando una unión de objetos representa casos que se excluyen, añade siempre el discriminante (tipo, kind, estado…). Además del error de arriba, te da el estrechamiento automático en los switch, como se explica en la lección de uniones y estrechamiento.

El tipo {} acepta casi cualquier cosa

let cualquiera: {} = 42;
cualquiera = "hola";
cualquiera = [1, 2, 3];
cualquiera = () => "función";
cualquiera = null;

Ábrelo en el editor y cámbialo →

tsc --strictlínea 5: error TS2322: El tipo 'null' no se puede asignar al tipo '{}'.

Por qué: {} parece «un objeto vacío», pero como tipo significa «cualquier valor al que se le pueda pedir una propiedad»: todo menos null y undefined. Un número, un texto, un array y una función tienen propiedades (toFixed, length…), así que las cuatro primeras asignaciones compilan. Solo falla la última. Es el tipo estructural más amplio que existe: no pide ninguna propiedad, así que casi todo lo cumple.

Cómo evitarlo: para «cualquier valor» usa unknown; para «cualquier objeto» (no primitivos), object; para un objeto con claves de texto, Record<string, unknown>. El tipo {} solo tiene sentido en trucos muy concretos, como NonNullable.

Un array de perros puede acabar sin perros

class Animal {
  nombre = "animal";
}
class Perro extends Animal {
  ladrar() {
    return "guau";
  }
}
const perros: Perro[] = [new Perro()];
const animales: Animal[] = perros;
animales.push(new Animal());
console.log(perros.length);
console.log(perros[1].ladrar());

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.ts2
y despuésTypeError: perros[1].ladrar is not a function

Por qué: TypeScript deja usar un Perro[] donde se pide un Animal[], porque leer de él es seguro: todo perro es un animal. Pero escribir no lo es, y el compilador no distingue lo uno de lo otro. perros y animales son el mismo array con dos nombres; al meter un Animal por uno, el otro sigue creyendo que solo tiene perros. Es un hueco conocido y aceptado: cerrarlo obligaría a marcar como error muchísimo código correcto que solo lee los arrays.

Las variables perros y animales apuntan al mismo array; al añadir un Animal por animales, perros contiene un elemento que no es un Perro aunque su tipo diga lo contrarioDos nombres, un solo array: lo que entra por uno sale por el otroperrostipo Perro[]animalestipo Animal[]el mismo array en memoria[0] Perro[1] Animalanimales.push(new Animal())TypeScript sigue creyendo que perros solo tiene perros: perros[1].ladrar() compila y falla.

El tipo es de la variable, no del array: el compilador no sabe que dos variables comparten el mismo. Esquema: decodigo.com.
Cómo evitarlo: si una función solo lee un array, declara el parámetro como readonly Animal[]: así le puedes pasar un Perro[] sin miedo, porque no tiene push. Y desconfía de cualquier función que reciba un array de un tipo general y le añada elementos.

Los métodos son más permisivos que las funciones

interface ConMetodo {
  procesar(x: string | number): void;
}
interface ConPropiedad {
  procesar: (x: string | number) => void;
}
const soloTexto = {
  procesar(x: string) {
    console.log(x.toUpperCase());
  },
};
const a: ConMetodo = soloTexto;
const b: ConPropiedad = soloTexto;
a.procesar(42);

Ábrelo en el editor y cámbialo →

tsc --strictlínea 13: error TS2322: El tipo '{ procesar(x: string): void; }' no se puede asignar al tipo 'ConPropiedad'.
  Los tipos de propiedad 'procesar' no son compatibles.
    El tipo '(x: string) => void' no se puede asignar al tipo '(x: string | number) => void'.
      Los tipos de parámetros 'x' y 'x' no son compatibles.
        El tipo 'string | number' no se puede asignar al tipo 'string'.
          El tipo 'number' no se puede asignar al tipo 'string'.

Solo hay un error, y no es el que cabría esperar. ConMetodo y ConPropiedad piden lo mismo: algo que sepa procesar textos y números. soloTexto solo sabe procesar textos. TypeScript lo rechaza cuando el tipo está escrito como propiedad con una función (procesar: (x) => void), pero lo acepta cuando está escrito como método (procesar(x): void). Y lo que se acepta, falla:

interface ConMetodo {
  procesar(x: string | number): void;
}
const soloTexto = {
  procesar(x: string) {
    console.log(x.toUpperCase());
  },
};
const a: ConMetodo = soloTexto;
a.procesar("hola");
a.procesar(42);

Ábrelo en el editor y cámbialo →

node archivo.tsHOLA
y despuésTypeError: x.toUpperCase is not a function

Por qué: con la opción strictFunctionTypes (incluida en strict), los parámetros de las funciones se comprueban bien: una función que solo acepta string no sirve donde se pueden recibir números. Pero los métodos quedaron fuera a propósito, porque la biblioteca estándar y el DOM están llenos de métodos declarados así, y comprobarlos con rigor rompería código que funciona.

Cómo evitarlo: en tus propias interfaces, declara las funciones como propiedades, procesar: (x: string | number) => void, y tendrás la comprobación estricta. Hay una regla de ESLint que lo exige: @typescript-eslint/method-signature-style.

Object.keys devuelve string[]

const precios = { cafe: 30, te: 25 };
for (const clave of Object.keys(precios)) {
  console.log(clave, precios[clave]);
}

Ábrelo en el editor y cámbialo →

tsc --strictlínea 3: error TS7053: El elemento tiene un tipo "any" de forma implícita porque la expresión de tipo "string" no se puede usar para indexar el tipo "{ cafe: number; te: number; }".
  No se encontró ninguna signatura de índice con un parámetro de tipo "string" en el tipo "{ cafe: number; te: number; }".

Por qué: Object.keys devuelve string[], no ("cafe" | "te")[], y parece un descuido. No lo es: por el tipado estructural, un objeto puede tener más propiedades de las que dice su tipo, y Object.keys las devuelve todas. Este ejemplo lo demuestra forzando el tipo de la clave con as:

interface Precios {
  cafe: number;
  te: number;
}
function sumar(p: Precios) {
  let total = 0;
  for (const clave of Object.keys(p)) {
    total += p[clave as keyof Precios];
  }
  return total;
}
const conMas = { cafe: 30, te: 25, nota: "precios de octubre" };
console.log(sumar(conMas));

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.ts55precios de octubre

Al sumar las propiedades, la tercera, nota, que el tipo no menciona, se coló como texto: 30 + 25 son 55, y después se concatenó la nota. Si TypeScript prometiera que las claves son solo "cafe" | "te", estaría mintiendo.

El tipo Precios solo garantiza cafe y te; el objeto real puede traer más propiedades, como nota, y Object.keys también las devuelveEl tipo dice lo mínimo que hay, no todo lo que hayLo que ve TypeScript: Precioscafe: numberte: numberLo que llega de verdadcafe: 30te: 25nota: "precios…"Por eso Object.keys(p) no puede prometer "cafe" | "te": promete string.

Un tipo describe lo que un objeto tiene como mínimo. Object.keys devuelve lo que tiene de verdad. Esquema: decodigo.com.
Cómo evitarlo: para recorrer claves y valores, Object.entries(precios). Si de verdad controlas el objeto (lo acabas de crear tú), puedes usar Object.keys(precios) as (keyof typeof precios)[], sabiendo que es una promesa tuya. Y para diccionarios con claves dinámicas, mejor un Map o un Record<string, number>.

Para TypeScript, un array nunca se sale de rango

const nombres = ["Ana", "Luis"];
const tercero = nombres[2];
console.log(tercero.toUpperCase());

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.tsTypeError: Cannot read properties of undefined (reading 'toUpperCase')

Por qué: el tipo de nombres[2] es string, sin más. TypeScript no sabe cuántos elementos tendrá un array al ejecutar, y si cada acceso por índice devolviera string | undefined, habría que comprobar hasta lista[i] dentro de un for que ya controla el límite. Así que, por defecto, supone que el índice existe. Lo mismo pasa con los diccionarios del tipo Record<string, ...>. Existe una opción, fuera de strict, que cambia la regla:

const nombres = ["Ana", "Luis"];
const tercero = nombres[2];
console.log(tercero.toUpperCase());
tsc --strictlínea 3: error TS18048: "tercero" es posiblemente "undefined".

Ese mensaje sale al activar noUncheckedIndexedAccess. Con ella, la línea 3 da string | undefined y hay que comprobarla antes de usarla.

Cómo evitarlo: añade "noUncheckedIndexedAccess": true a tu tsconfig.json en los proyectos nuevos; obliga a comprobar, pero es exactamente la comprobación que evita este fallo. Para recorrer arrays, for...of no tiene el problema: nunca da un elemento que no existe.

El estrechamiento se olvida dentro de las funciones

const estado: { mensaje: string | null } = { mensaje: "hola" };
if (estado.mensaje !== null) {
  console.log(estado.mensaje.length);
  setTimeout(() => {
    console.log(estado.mensaje.length);
  }, 0);
}

Ábrelo en el editor y cámbialo →

tsc --strictlínea 5: error TS18047: "estado.mensaje" es posiblemente "null".

La línea 3 compila y la 5, idéntica, no. Por qué: después del if, TypeScript sabe que estado.mensaje no es null… en ese momento. Pero la función de setTimeout se ejecuta más tarde, y para entonces cualquier otro código pudo cambiar estado.mensaje a null. Como el compilador no puede saber cuándo se ejecutará una función, en las propiedades de objetos (y en las variables con let que se reasignan después) descarta el estrechamiento al entrar en ella. Tiene razón en desconfiar.

La solución es copiar el valor a una const: una constante no puede cambiar, así que el estrechamiento sobrevive dentro de la función, aunque el objeto cambie después:

const estado: { mensaje: string | null } = { mensaje: "hola" };
const mensaje = estado.mensaje;
if (mensaje !== null) {
  setTimeout(() => {
    console.log(mensaje.length);
  }, 0);
}
estado.mensaje = null;

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.ts4
Cómo evitarlo: antes de un callback, guarda en una const lo que ya comprobaste. Además del error, te ahorras un fallo real: el de leer un valor que cambió mientras esperabas.

readonly solo protege el primer nivel

const config: Readonly<{ servidor: { puerto: number } }> = {
  servidor: { puerto: 80 },
};
config.servidor.puerto = 8080;
console.log(config.servidor.puerto);

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.ts8080

Por qué: Readonly<T> (y la palabra readonly) solo protege las propiedades del primer nivel. Reemplazar el servidor entero sí da error:

const config: Readonly<{ servidor: { puerto: number } }> = {
  servidor: { puerto: 80 },
};
config.servidor = { puerto: 8080 };

Ábrelo en el editor y cámbialo →

tsc --strictlínea 4: error TS2540: No se puede asignar a "servidor" porque es una propiedad de solo lectura.

Pero cambiar una propiedad del objeto de dentro, no. Además, readonly solo existe al compilar: el JavaScript resultante no protege nada, igual que const en JavaScript impide reasignar la variable pero no modificar el objeto.

Cómo evitarlo: para datos fijos escritos en el código, as const los hace de solo lectura en todos los niveles. Para estructuras recibidas, un tipo recursivo como type Profundo<T> = { readonly [K in keyof T]: Profundo<T[K]> }. Y si necesitas la protección también al ejecutar, Object.freeze, que tampoco es profundo.

Una función void puede devolver algo

type Avisar = () => void;
const avisar: Avisar = () => 42;
const r = avisar();
console.log(r);

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.ts42

Por qué: en un tipo de función, void no significa «no devuelve nada», sino «lo que devuelva, no lo voy a usar». Es lo que permite escribir lista.forEach((n) => otra.push(n)): push devuelve un número, pero forEach espera una función void y la acepta igual. El valor sigue ahí: r tiene tipo void y vale 42. En cambio, en una declaración de función, void sí es una promesa:

function avisar(): void {
  return 42;
}

Ábrelo en el editor y cámbialo →

tsc --strictlínea 2: error TS2322: El tipo 'number' no se puede asignar al tipo 'void'.
Cómo evitarlo: no uses nunca el resultado de una función de tipo void: para TypeScript no existe, pero al ejecutar puede ser cualquier cosa. Si necesitas que una función no devuelva nada, escríbelo en su declaración, no solo en el tipo de una variable.

enum: el tipo que sí existe al ejecutar

enum Estado {
  Activo,
  Pausado,
}
console.log(Estado);
const n: number = 42;
const e: Estado = n;
console.log(e);

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.tsSyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode

Por qué: casi todo TypeScript son tipos que se borran al compilar: quitas las anotaciones y queda JavaScript válido. enum es la gran excepción, porque genera código. Node 24 ejecuta TypeScript precisamente borrando los tipos, sin compilar, y un enum no se puede borrar: Node se niega. Compilado con tsc, esto es lo que se ejecuta:

var Estado;
(function (Estado) {
    Estado[Estado["Activo"] = 0] = "Activo";
    Estado[Estado["Pausado"] = 1] = "Pausado";
})(Estado || (Estado = {}));
console.log(Estado);
const n = 42;
const e = n;
console.log(e);
node (el .js compilado){ '0': 'Activo', '1': 'Pausado', Activo: 0, Pausado: 1 }
42

Dos sorpresas más. El objeto tiene las claves en los dos sentidos (Estado.Activo es 0 y Estado[0] es "Activo"), así que Object.keys(Estado) devuelve cuatro claves, no dos. Y un enum numérico acepta cualquier número que llegue en una variable de tipo number: e vale 42, que no es ningún estado. Solo un literal fuera de rango, como const e: Estado = 42, da error.

Cómo evitarlo: en código nuevo, una unión de literales hace lo mismo sin generar código: type Estado = "activo" | "pausado". Si necesitas también la lista de valores al ejecutar, un objeto as const. Hay una opción, erasableSyntaxOnly, que marca como error todo lo que Node no puede borrar: enum, namespace con código y los parámetros con public en los constructores.

Las interfaces no existen al ejecutar

interface Usuario {
  nombre: string;
}
function esUsuario(x: unknown) {
  return x instanceof Usuario;
}

Ábrelo en el editor y cámbialo →

tsc --strictlínea 5: error TS2693: 'Usuario' solo hace referencia a un tipo, pero aquí se usa como valor.

Por qué: instanceof se ejecuta en JavaScript, y una interface no existe en JavaScript: se borra al compilar. Al ejecutar no hay nada con lo que comparar. Pasa lo mismo con cualquier tipo: no se puede preguntar «¿esto es un Usuario?» en tiempo de ejecución. Hay que preguntar por lo que tiene, y una guarda de tipo (x is Usuario) le transmite la respuesta al compilador:

interface Usuario {
  nombre: string;
}
function esUsuario(x: unknown): x is Usuario {
  return typeof x === "object" && x !== null && "nombre" in x && typeof x.nombre === "string";
}
console.log(esUsuario({ nombre: "Ana" }), esUsuario({ nombre: 7 }), esUsuario(null));

Ábrelo en el editor y cámbialo →

tsc --strictSin errores.
node archivo.tstrue false false
Cómo evitarlo: si necesitas instanceof, usa una class, que sí existe al ejecutar. Para datos que llegan como JSON (que nunca son instancias de nada), una guarda de tipo como la de arriba o un validador. Ojo: TypeScript no comprueba que la guarda diga la verdad; si su código está mal, el error vuelve a ser tuyo.

Resumen: la regla detrás de cada rareza

RarezaLa causaLa costumbre que la evita
any, asDesactivan la revisión a petición tuyaunknown y validar lo que llega de fuera
Propiedades de más, unionesTipado estructural: sobrar no es un errorLiterales en el sitio y uniones discriminadas
{}Un tipo que no pide nada lo cumple todounknown u object
Arrays, métodosHuecos aceptados para no romper código correctoreadonly en parámetros y funciones como propiedades
Object.keysUn objeto puede tener más de lo que dice su tipoObject.entries
ÍndicesEl compilador no sabe la longitud de un arraynoUncheckedIndexedAccess
Estrechamiento en callbacksLa función se ejecuta más tardeCopiar a una const
readonlySolo protege el primer nivelas const
voidEn un tipo de función significa «no lo uso»No usar nunca ese valor
enum, interfacesUnos tipos generan código y otros se borranUniones de literales, guardas de tipo

¿Quieres comprobar si ya piensas como el compilador? Prueba el test «¿compila o no?», o abre cualquiera de estos fragmentos en el editor de TypeScript y cámbialo hasta entender por qué compila o deja de hacerlo.

Todo el código de esta página se comprobó con TypeScript 7.0.2 (tsc --strict --locale es) y se ejecutó con Node.js 24.22 en un contenedor limpio; los mensajes del compilador y las salidas están copiados de esa ejecución.