Procesamiento de JSON en TypeScript: desde operaciones básicas hasta tipado avanzado
JSON (JavaScript Object Notation) sigue siendo el formato principal de intercambio de datos en el desarrollo web. TypeScript añade al trabajo con JSON un potente sistema de tipos que permite evitar muchos errores en la fase de compilación. En este artículo, analizaremos todos los aspectos del procesamiento de JSON en TypeScript: desde el análisis simple hasta la creación de validadores estrictamente tipados.
Análisis y serialización básicos
Comencemos con las operaciones fundamentales. Los métodos incorporados JSON.parse() y JSON.stringify() funcionan en TypeScript igual que en JavaScript, pero con diferencias importantes en el tipado.
// Análisis simple: el resultado será anyconst data: any = JSON.parse('{"name": "Alice", "age": 30}');
// Mejor especificar el tipo de inmediatointerface User { name: string; age: number;}
const user: User = JSON.parse('{"name": "Alice", "age": 30}') as User;console.log(user.name); // AliceEs importante entender: as User es una aserción de tipo (type assertion), no una verificación. Si el JSON contiene datos no válidos, TypeScript no le advertirá del error. Para un trabajo seguro, se necesita validación.
Análisis seguro con Generics
Creemos una función universal para el análisis seguro de JSON con verificación de estructura:
function safeParse<T>(json: string, validator: (data: unknown) => data is T): T | null { try { const parsed: unknown = JSON.parse(json); if (validator(parsed)) { return parsed; } return null; } catch { return null; }}
// Definimos el tipo y la función validadorainterface Product { id: number; title: string; price: number;}
function isProduct(data: unknown): data is Product { if (typeof data !== 'object' || data === null) return false; const obj = data as Record<string, unknown>; return ( typeof obj.id === 'number' && typeof obj.title === 'string' && typeof obj.price === 'number' );}
const jsonString = '{"id": 1, "title": "Book", "price": 19.99}';const product = safeParse<Product>(jsonString, isProduct);if (product) { console.log(`Producto: ${product.title}, precio: ${product.price}`);} else { console.log('Formato JSON no válido');}Trabajo con estructuras anidadas y arrays
Los datos JSON reales a menudo contienen objetos y arrays anidados. Veamos un ejemplo con tipado de una estructura compleja:
interface Order { id: string; customer: { name: string; email: string; }; items: Array<{ productId: number; quantity: number; }>; total: number;}
// Analizamos y verificamos la estructura inmediatamentefunction parseOrder(json: string): Order { const raw = JSON.parse(json); // Verificación simple if (!raw.id || !raw.customer || !Array.isArray(raw.items)) { throw new Error('Estructura de pedido no válida'); } return raw as Order;}
const orderJson = `{ "id": "ORD-001", "customer": {"name": "Bob", "email": "bob@example.com"}, "items": [{"productId": 42, "quantity": 2}], "total": 59.98}`;
try { const order = parseOrder(orderJson); console.log(`Pedido ${order.id} por un total de ${order.total}`);} catch (e) { console.error('Error de análisis:', e.message);}Serialización con convertidores personalizados
A veces es necesario controlar cómo se convierten los objetos a JSON. TypeScript permite crear serializadores personalizados:
interface Config { host: string; port: number; timeout: number; // en milisegundos}
function serializeConfig(config: Config): string { return JSON.stringify({ host: config.host, port: config.port, timeout: config.timeout / 1000 // convertimos a segundos }, null, 2); // formato con sangría}
const myConfig: Config = { host: 'localhost'