Bun es un runtime JavaScript y TypeScript escrito en Zig que ejecuta archivos .ts directamente, sin pasos de compilación previos. Además de ser un runtime, incluye un bundler, un test runner y un gestor de paquetes compatible con npm.
Ejecutar TypeScript directamente
// hola.ts const mensaje: string = '¡Hola desde Bun!'; console.log(mensaje); // Ejecutar sin compilar: // bun hola.ts // Modo watch: // bun --watch hola.ts
Bun usa transpilación en lugar de type-checking: ejecuta el código TypeScript pero no comprueba los tipos. Para la verificación de tipos hay que seguir usando tsc --noEmit.
Bun.build: empaquetar proyectos
// build.ts
const resultado = await Bun.build({
entrypoints: ['./src/index.ts'],
outdir: './dist',
target: 'node', // 'node' | 'browser' | 'bun'
format: 'esm', // 'esm' | 'cjs' | 'iife'
minify: true,
sourcemap: 'external',
define: {
'process.env.NODE_ENV': '"production"',
},
});
if (!resultado.success) {
console.error(resultado.logs);
process.exit(1);
}
// Bun.build devuelve BuildOutput tipado:
for (const artifact of resultado.outputs) {
console.log(artifact.path, artifact.size); // string, number
}
Test runner con bun test
// calculadora.test.ts
import { describe, it, expect, mock, beforeEach } from 'bun:test';
import { sumar, obtenerDatos } from './calculadora';
describe('sumar', () => {
it('suma correctamente', () => {
expect(sumar(2, 3)).toBe(5);
});
it('rechaza strings en compilación', () => {
// sumar('2', 3); // Error de TypeScript
expect(sumar(0, 0)).toBe(0);
});
});
// Mocks tipados:
const mockFetch = mock(() => Promise.resolve({ json: () => ({ datos: [] }) }));
beforeEach(() => {
mockFetch.mockClear();
});
Bun.file: API de ficheros tipada
// Leer un fichero:
const fichero = Bun.file('./datos.json');
// BunFile con tipo inferido
const texto = await fichero.text(); // string
const buffer = await fichero.arrayBuffer(); // ArrayBuffer
const json = await fichero.json(); // any (cast manual recomendado)
interface Datos { nombre: string; edad: number; }
const datos: Datos = await Bun.file('./datos.json').json();
// Escribir un fichero:
await Bun.write('./salida.txt', 'Contenido del fichero');
await Bun.write('./salida.json', JSON.stringify({ ok: true }));
Bun.serve: servidor HTTP tipado
const servidor = Bun.serve({
port: 3000,
fetch(req: Request): Response | Promise<Response> {
const url = new URL(req.url);
if (url.pathname === '/ping') {
return new Response('pong');
}
if (url.pathname === '/json') {
return Response.json({ ok: true, ts: Date.now() });
}
return new Response('No encontrado', { status: 404 });
},
});
console.log(`Servidor en http://localhost:${servidor.port}`);
Bun.env: variables de entorno tipadas
// Bun.env es un Record<string, string | undefined>
const puerto = Bun.env.PORT ?? '3000';
const modoProduccion = Bun.env.NODE_ENV === 'production';
// Para tipar las variables de entorno de forma estricta,
// se puede usar una librería de validación:
import { z } from 'zod';
const env = z.object({
DATABASE_URL: z.string().url(),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
}).parse(Bun.env);
const db = await conectar(env.DATABASE_URL); // string, nunca undefined
Imagen: Pexels / Markus Spiske
