Convertir JSON a TypeScript: Generación de Tipos y Validación
Aprende a generar tipos TypeScript desde JSON, usar quicktype, json2ts y Zod para validación. Guía completa con ejemplos prácticos.
Big JSON Team
• Technical WriterExpert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.
# Convertir JSON a TypeScript: Generación de Tipos y Validación
TypeScript mejora significativamente el trabajo con JSON mediante tipos estáticos. Esta guía completa te enseñará cómo generar tipos TypeScript desde JSON y validar datos en tiempo de ejecución.
¿Por Qué Tipos TypeScript para JSON?
Ventajas
// ❌ Sin tipos (JavaScript)
const usuario = JSON.parse(response);
console.log(usuario.naem); // Typo - error en runtime
// ✅ Con tipos (TypeScript)
interface Usuario {
nombre: string;
email: string;
edad: number;
}
const usuario: Usuario = JSON.parse(response);
console.log(usuario.naem); // Error en compile-time ✓
Beneficios:
- ✅ Detección de errores en tiempo de compilación
- ✅ Autocompletado en IDE
- ✅ Refactoring seguro
- ✅ Documentación automática
- ✅ Mejor mantenibilidad
Crear Tipos Manualmente
Interfaz Básica
// JSON
{
"id": 1,
"nombre": "Ana García",
"email": "ana@ejemplo.com",
"edad": 28,
"activo": true
}
// Tipo TypeScript correspondiente
interface Usuario {
id: number;
nombre: string;
email: string;
edad: number;
activo: boolean;
}
Tipos Anidados
// JSON anidado
{
"usuario": {
"nombre": "Carlos López",
"contacto": {
"email": "carlos@ejemplo.com",
"telefono": "+34600123456"
},
"direcciones": [
{
"tipo": "casa",
"calle": "Calle Mayor 123",
"ciudad": "Madrid"
}
]
}
}
// Tipos TypeScript
interface Contacto {
email: string;
telefono: string;
}
interface Direccion {
tipo: 'casa' | 'trabajo';
calle: string;
ciudad: string;
}
interface Usuario {
nombre: string;
contacto: Contacto;
direcciones: Direccion[];
}
interface RespuestaAPI {
usuario: Usuario;
}
Tipos Opcionales
interface Usuario {
id: number;
nombre: string;
email: string;
telefono?: string; // Opcional
edad?: number; // Opcional
biografia?: string | null; // Puede ser null
}
// Uso
const usuario1: Usuario = {
id: 1,
nombre: "Ana",
email: "ana@ejemplo.com"
// telefono y edad son opcionales
};
const usuario2: Usuario = {
id: 2,
nombre: "Carlos",
email: "carlos@ejemplo.com",
telefono: "+34600123456",
edad: 35,
biografia: null
};
Generar Tipos Automáticamente
quicktype
Instalación:npm install -g quicktype
Desde archivo JSON:
# Generar tipos desde archivo
quicktype usuarios.json -o usuarios.ts
# Con nombre de tipo personalizado
quicktype usuarios.json -o usuarios.ts --top-level Usuario
# Desde URL
quicktype https://api.ejemplo.com/usuarios -o tipos.ts
# Múltiples archivos
quicktype usuarios.json productos.json -o tipos.ts
Ejemplo:
// usuarios.json
{
"usuarios": [
{
"id": 1,
"nombre": "Ana",
"email": "ana@ejemplo.com",
"edad": 28
}
]
}
quicktype usuarios.json -o tipos.ts --lang typescript
Resultado (tipos.ts):
export interface RespuestaUsuarios {
usuarios: Usuario[];
}
export interface Usuario {
id: number;
nombre: string;
email: string;
edad: number;
}
Opciones avanzadas:
# Con validación runtime
quicktype usuarios.json -o tipos.ts --runtime-typecheck
# Solo interfaces (sin validación)
quicktype usuarios.json -o tipos.ts --just-types
# Con readonly
quicktype usuarios.json -o tipos.ts --readonly
# Nomenclatura personalizada
quicktype usuarios.json -o tipos.ts --acronym-style camel
json-to-ts (Online)
URL: transform.tools/json-to-typescript Uso:VS Code Extensions
JSON to TS:1. Instalar extensión "JSON to TS"
Copiar JSON
Ctrl+Shift+P → "Convert JSON to TypeScript Interfaces"
Pegar resultado
Paste JSON as Code:
1. Instalar "Paste JSON as Code"
Copiar JSON
Ctrl+Shift+P → "Paste JSON as Types"
TypeScript generado automáticamente
Validación en Runtime con Zod
¿Por Qué Zod?
TypeScript valida en compile-time, pero necesitas validar datos de APIs en runtime:
// TypeScript no puede garantizar esto en runtime
const respuesta = await fetch('/api/usuarios');
const datos: Usuario = await respuesta.json(); // ¿Realmente es Usuario?
Instalación de Zod
npm install zod
Schema Básico
import { z } from 'zod';
// Definir schema
const UsuarioSchema = z.object({
id: z.number(),
nombre: z.string(),
email: z.string().email(),
edad: z.number().min(0).max(150),
activo: z.boolean().default(true)
});
// Inferir tipo TypeScript desde schema
type Usuario = z.infer<typeof UsuarioSchema>;
// Validar datos
const datos = {
id: 1,
nombre: "Ana García",
email: "ana@ejemplo.com",
edad: 28,
activo: true
};
try {
const usuario = UsuarioSchema.parse(datos);
console.log("✓ Datos válidos:", usuario);
} catch (error) {
if (error instanceof z.ZodError) {
console.error("✗ Errores:", error.errors);
}
}
Schemas Complejos
import { z } from 'zod';
// Enums
const RolSchema = z.enum(['admin', 'editor', 'usuario']);
// Objetos anidados
const DireccionSchema = z.object({
calle: z.string(),
ciudad: z.string(),
codigoPostal: z.string().regex(/^d{5}$/)
});
// Arrays
const HabilidadesSchema = z.array(z.string()).min(1).max(10);
// Uniones
const ContactoSchema = z.union([
z.object({ tipo: z.literal('email'), valor: z.string().email() }),
z.object({ tipo: z.literal('telefono'), valor: z.string() })
]);
// Schema completo
const UsuarioCompletoSchema = z.object({
id: z.number().positive(),
nombre: z.string().min(2).max(100),
email: z.string().email(),
edad: z.number().min(18).optional(),
rol: RolSchema,
direccion: DireccionSchema.optional(),
habilidades: HabilidadesSchema,
contactos: z.array(ContactoSchema),
fechaRegistro: z.string().datetime(),
metadata: z.record(z.string(), z.any()).optional()
});
type UsuarioCompleto = z.infer<typeof UsuarioCompletoSchema>;
Transformaciones
import { z } from 'zod';
const UsuarioSchema = z.object({
nombre: z.string().trim().toLowerCase(),
edad: z.string().transform(val => parseInt(val)),
email: z.string().email().transform(val => val.toLowerCase()),
fechaNacimiento: z.string().transform(val => new Date(val)),
tags: z.string().transform(val => val.split(',').map(t => t.trim()))
});
const datos = {
nombre: " ANA GARCÍA ",
edad: "28",
email: "Ana@Ejemplo.COM",
fechaNacimiento: "1996-01-15",
tags: "javascript, typescript, react"
};
const usuario = UsuarioSchema.parse(datos);
console.log(usuario);
/
{
nombre: "ana garcía",
edad: 28,
email: "ana@ejemplo.com",
fechaNacimiento: Date(1996-01-15),
tags: ["javascript", "typescript", "react"]
}
/
Validación de API
import { z } from 'zod';
const UsuarioSchema = z.object({
id: z.number(),
nombre: z.string(),
email: z.string().email()
});
const RespuestaAPISchema = z.object({
status: z.literal('success'),
data: z.array(UsuarioSchema),
pagination: z.object({
page: z.number(),
total: z.number()
})
});
type RespuestaAPI = z.infer<typeof RespuestaAPISchema>;
async function obtenerUsuarios(): Promise<RespuestaAPI> {
const respuesta = await fetch('/api/usuarios');
const datos = await respuesta.json();
// Validar respuesta
return RespuestaAPISchema.parse(datos);
}
// Uso
try {
const resultado = await obtenerUsuarios();
console.log("Usuarios:", resultado.data);
} catch (error) {
if (error instanceof z.ZodError) {
console.error("Respuesta de API inválida:", error.errors);
}
}
io-ts para Validación
Instalación
npm install io-ts fp-ts
Uso Básico
import * as t from 'io-ts';
import { isRight } from 'fp-ts/Either';
// Definir codec
const UsuarioCodec = t.type({
id: t.number,
nombre: t.string,
email: t.string,
edad: t.union([t.number, t.undefined])
});
type Usuario = t.TypeOf<typeof UsuarioCodec>;
// Validar
const datos = {
id: 1,
nombre: "Carlos",
email: "carlos@ejemplo.com"
};
const resultado = UsuarioCodec.decode(datos);
if (isRight(resultado)) {
const usuario = resultado.right;
console.log("✓ Válido:", usuario);
} else {
console.error("✗ Inválido:", resultado.left);
}
Generación Automática desde OpenAPI/Swagger
openapi-typescript
npm install -D openapi-typescript
# Generar tipos desde OpenAPI spec
npx openapi-typescript https://api.ejemplo.com/openapi.json -o tipos.ts
# Desde archivo local
npx openapi-typescript ./openapi.yaml -o tipos.ts
Uso:
import type { paths } from './tipos';
type UsuariosResponse = paths['/usuarios']['get']['responses']['200']['content']['application/json'];
async function obtenerUsuarios(): Promise<UsuariosResponse> {
const respuesta = await fetch('/usuarios');
return respuesta.json();
}
Mejores Prácticas
1. Centraliza Definiciones de Tipos
// types/usuario.ts
export interface Usuario {
id: number;
nombre: string;
email: string;
}
export interface CrearUsuarioDTO {
nombre: string;
email: string;
password: string;
}
export interface ActualizarUsuarioDTO {
nombre?: string;
email?: string;
}
// Usar en toda la aplicación
import { Usuario } from './types/usuario';
2. Usa Utility Types
interface Usuario {
id: number;
nombre: string;
email: string;
password: string;
fechaCreacion: Date;
}
// Sin password
type UsuarioPublico = Omit<Usuario, 'password'>;
// Solo id y nombre
type UsuarioResumen = Pick<Usuario, 'id' | 'nombre'>;
// Todo opcional
type UsuarioParcial = Partial<Usuario>;
// Todo requerido
type UsuarioCompleto = Required<Usuario>;
// Todo readonly
type UsuarioInmutable = Readonly<Usuario>;
// Para crear (sin id ni fechaCreacion)
type CrearUsuario = Omit<Usuario, 'id' | 'fechaCreacion'>;
3. Validación con Type Guards
interface Usuario {
id: number;
nombre: string;
email: string;
}
// Type guard
function esUsuario(obj: any): obj is Usuario {
return (
typeof obj === 'object' &&
obj !== null &&
typeof obj.id === 'number' &&
typeof obj.nombre === 'string' &&
typeof obj.email === 'string'
);
}
// Uso
const datos = JSON.parse(respuesta);
if (esUsuario(datos)) {
// TypeScript sabe que datos es Usuario
console.log(datos.nombre);
} else {
throw new Error('Datos inválidos');
}
4. Tipos Discriminados
type Evento =
| { tipo: 'click'; x: number; y: number }
| { tipo: 'keypress'; tecla: string }
| { tipo: 'scroll'; posicion: number };
function manejarEvento(evento: Evento) {
switch (evento.tipo) {
case 'click':
console.log(Click en ${evento.x}, ${evento.y});
break;
case 'keypress':
console.log(Tecla presionada: ${evento.tecla});
break;
case 'scroll':
console.log(Scroll a posición: ${evento.posicion});
break;
}
}
5. Genéricos para Respuestas API
interface RespuestaAPI<T> {
status: 'success' | 'error';
data?: T;
error?: {
code: string;
message: string;
};
meta?: {
timestamp: string;
version: string;
};
}
interface RespuestaPaginada<T> extends RespuestaAPI<T[]> {
pagination: {
page: number;
perPage: number;
total: number;
};
}
// Uso
type RespuestaUsuarios = RespuestaPaginada<Usuario>;
type RespuestaProductos = RespuestaPaginada<Producto>;
Herramientas de Desarrollo
tsc --noEmit
Validar tipos sin generar JavaScript:
npx tsc --noEmit
ts-node
Ejecutar TypeScript directamente:
npm install -D ts-node
npx ts-node script.ts
Type Coverage
Verificar cobertura de tipos:
npm install -D type-coverage
npx type-coverage
Integración con Frameworks
Express + TypeScript
import express, { Request, Response } from 'express';
import { z } from 'zod';
const app = express();
app.use(express.json());
const CrearUsuarioSchema = z.object({
nombre: z.string().min(2),
email: z.string().email(),
edad: z.number().min(18)
});
app.post('/usuarios', (req: Request, res: Response) => {
try {
const datos = CrearUsuarioSchema.parse(req.body);
// datos es tipado correctamente
const usuario = crearUsuario(datos);
res.json(usuario);
} catch (error) {
if (error instanceof z.ZodError) {
res.status(400).json({ errores: error.errors });
}
}
});
React + TypeScript
import { useState, useEffect } from 'react';
import { z } from 'zod';
const UsuarioSchema = z.object({
id: z.number(),
nombre: z.string(),
email: z.string().email()
});
type Usuario = z.infer<typeof UsuarioSchema>;
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState<Usuario[]>([]);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetch('/api/usuarios')
.then(res => res.json())
.then(datos => {
// Validar cada usuario
const usuariosValidos = datos.map((u: any) =>
UsuarioSchema.parse(u)
);
setUsuarios(usuariosValidos);
})
.catch(err => setError(err.message));
}, []);
return (
<div>
{usuarios.map(u => (
<div key={u.id}>{u.nombre}</div>
))}
</div>
);
}
Conclusión
Convertir JSON a tipos TypeScript mejora significativamente la seguridad y mantenibilidad del código. Herramientas como quicktype facilitan la generación automática, mientras que Zod proporciona validación en runtime.
Puntos Clave
- Usa quicktype para generar tipos automáticamente
- Implementa validación runtime con Zod o io-ts
- Centraliza definiciones de tipos
- Aprovecha utility types de TypeScript
- Valida datos de APIs siempre
¡Trabaja con JSON de forma type-safe en TypeScript!
Artículos Relacionados
JavaScript y JSON: Guía Completa de JSON.parse() y JSON.stringify()
Domina el trabajo con JSON en JavaScript. Aprende JSON.parse(), JSON.stringify() y mejores prácticas para aplicaciones web modernas.
Entendiendo JSON Schema: Validación y Documentación de Datos
Aprende a usar JSON Schema para validar y documentar tus estructuras JSON. Guía completa con ejemplos prácticos en JavaScript y Python.
Errores Comunes JSON y Cómo Solucionarlos: Guía de Debugging
Aprende a identificar y resolver los errores JSON más comunes. Incluye ejemplos prácticos de debugging y mejores prácticas de validación.