← Volver al Blog

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 Team13 min de lecturaDesarrollo
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

13 min lectura

# 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:
  • Pegar JSON
  • Copiar TypeScript generado
  • Ajustar según necesidad
  • 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!

    Share:

    Artículos Relacionados

    Read in English