Escribir definiciones de tipo para respuestas de API es inevitable en el desarrollo con TypeScript. Escribir a mano una interface mientras lees un objeto JSON profundamente anidado es tedioso y un caldo de cultivo para errores tipográficos.

Cómo generar tipos de TypeScript a partir de JSON: guía práctica con ejemplos

Este artículo muestra en qué tipos de TypeScript se convierte el JSON real, con ejemplos concretos de conversión, junto con los consejos y advertencias de la generación automática.

Lo básico: un objeto plano

Empecemos con un ejemplo sencillo.

{
  "id": 42,
  "name": "Alice",
  "isActive": true
}

Esto se convierte en el siguiente tipo. El tipo de cada valor (number / string / boolean) se infiere mecánicamente a partir de los datos reales.

interface User {
  id: number;
  name: string;
  isActive: boolean;
}

Anidamiento y arreglos

Las respuestas de API reales contienen objetos anidados y arreglos.

{
  "id": 42,
  "name": "Alice",
  "roles": ["admin", "editor"],
  "profile": {
    "age": 30,
    "city": "Tokyo"
  }
}

La convención es tipar los arreglos a partir de su tipo de elemento (string[]) y extraer los objetos anidados en sus propias interfaces.

interface Profile {
  age: number;
  city: string;
}

interface User {
  id: number;
  name: string;
  roles: string[];
  profile: Profile;
}

Trampas fáciles de pasar por alto

null y propiedades opcionales

Cuando el JSON contiene null, ese valor por sí solo no te dice si el campo es “siempre anulable” o “simplemente resultó ser null”.

{ "nickname": null, "deletedAt": null }

Una conversión mecánica produce nickname: null, pero en la práctica string | null o nickname?: string suele ser lo correcto. Ajústalo a mano tras la generación para que coincida con la especificación de la API.

interface User {
  nickname: string | null;   // también puede llegar un valor
  deletedAt?: string;        // el campo en sí puede no estar
}

Cuidado con los números como cadenas

Las API que envuelven números en cadenas, como "price": "1980", son comunes. Los generadores lo infieren como string. Si vas a hacer cálculos, cambia el tipo a number o convierte al recibir; decide una política de antemano.

Los arreglos vacíos no tienen tipo determinable

Un arreglo vacío como "tags": [] no da tipo de elemento y tiende a convertirse en never[] o any[]. Usa una muestra con elementos, o especifica string[] a mano tras generar.

Pasos para usar la generación automática

  1. Copia una respuesta real de la pestaña Network del navegador o de la salida de curl
  2. Pégala en el generador de JSON → TypeScript
  3. Renombra la interfaz raíz de Root a algo como UserResponse
  4. Ajusta los campos null y opcionales según la especificación
  5. Pega en tu proyecto: listo

La clave es usar una muestra representativa con todos los campos presentes. Una muestra pobre se perderá campos opcionales y variaciones de tipo.

Cuando los tipos no bastan: recurre a Zod

Los tipos de TypeScript son una comprobación en tiempo de compilación. No hay garantía de que los datos que realmente llegan de una API externa coincidan con tus tipos. Si también quieres validación en tiempo de ejecución, genera un esquema con el generador de JSON → Zod y añade una comprobación de entrada con schema.parse(data). Zod además infiere tipos, lo que te permite gestionar tipos y validación en un solo lugar.

Preguntas frecuentes

Renombrar la interfaz raíz cada vez es tedioso.

La mayoría de las herramientas emiten la raíz como un genérico Root. Lo más fácil es hacer un buscar-y-reemplazar a la convención de tu proyecto (ApiResponse, UserProfile, …) tras copiar. Los nombres de las interfaces anidadas suelen reflejar su contenido, así que corregir solo la raíz basta en la mayoría de los casos.

¿Cómo uso una API en snake_case como camelCase?

Los tipos generados reflejan las claves del JSON, así que obtendrás nombres como created_at. Para estandarizar en camelCase, convierte en el límite de la API (una función de mapeo o una librería de camelización). Mantén los tipos acordes a la realidad de la API y haz la conversión explícita para evitar confusiones.

¿Debo crear primero el tipo de TypeScript o el esquema de Zod?

Si necesitas validación en tiempo de ejecución, crea el esquema de Zod y deriva el tipo con z.infer: así todo queda en un solo lugar. Si una comprobación en tiempo de compilación basta para datos internos de confianza, generar solo el tipo de TypeScript es suficiente. Usa Zod para “entradas no confiables” como API externas y datos de formularios.