Para convertir JSON a TypeScript, asigna cada valor JSON a su tipo: las cadenas se convierten en string, los números en number, los objetos en definiciones anidadas de interface, y los arreglos en T[]. Las partes difíciles son inferir campos opcionales frente a obligatorios, colapsar arreglos mixtos en tipos unión, y decidir cuándo también necesitas validación en tiempo de ejecución con Zod.
La generación automática maneja el 90% aburrido. El 10% restante, la parte en la que un conversor ingenuo se equivoca, es donde viven los errores. Una sola muestra de API no puede decirte qué campos son anulables, cuáles a veces faltan, o si un arreglo vacío es string[] o never[]. Esta guía explica los mecanismos y los problemas comunes, y luego muestra cómo generar la interfaz, un esquema Zod y una estructura Go a partir de un solo pegado.
El Mapeo Básico: Valores JSON a Tipos TypeScript#
Comience con una respuesta concreta de una API. Supongamos que está integrando un endpoint de usuario:
{
"id": 4821,
"username": "ada",
"email": "[email protected]",
"isActive": true,
"roles": ["admin", "editor"],
"profile": {
"displayName": "Ada Lovelace",
"avatarUrl": null
},
"lastLoginAt": "2026-05-30T11:04:00Z"
}
Una traducción directa y correcta se ve así:
interface User {
id: number;
username: string;
email: string;
isActive: boolean;
roles: string[];
profile: Profile;
lastLoginAt: string;
}
interface Profile {
displayName: string;
avatarUrl: string | null;
}
Aquí ya hay algunas decisiones que un generador descuidado pasaría por alto. Observe que avatarUrl es string | null, no string, porque el valor de ejemplo es null. Observe que lastLoginAt es string, no Date, porque JSON no tiene un tipo fecha, es solo una cadena ISO hasta que se analiza. Y profile se extrajo en su propia interfaz Profile con nombre en lugar de estar en línea, lo que mantiene los tipos reutilizables.
La tabla de mapeo principal es breve:
| JSON | TypeScript | Cuidado con |
|---|---|---|
"texto" | string | Las fechas ISO siguen siendo string |
42, 3.14 | number | No hay distinción int vs float |
true / false | boolean | |
null | null | Generalmente parte de una unión, ej. string | null |
{ ... } | interface / objeto anidado | Extraiga formas reutilizadas en interfaces con nombre |
[ ... ] | T[] | Arrays de tipos mixtos se convierten en una unión; arrays vacíos son ambiguos |
Interface vs Type Alias: cuál generar#
Tanto interface como type pueden describir la forma de un objeto, y para respuestas API simples son casi intercambiables. La guía práctica:
- Usa
interfacepara formas de objeto que puedas extender o que representen un contrato de API. Las interfaces admiten la fusión de declaraciones y producen mensajes de error más claros. - Usa
typecuando necesites uniones, intersecciones, tipos mapeados o tuplas que una interfaz no pueda expresar.
Para modelos derivados de JSON, elige interface por defecto. Solo recurre a type cuando los datos en sí sean una unión, como un campo que sea "pendiente" | "completado". La mayoría de los generadores permiten elegir el estilo de salida, y interface es la opción predeterminada más segura para una forma de API externa.
Los tres errores que cometen los generadores de una sola muestra#
Aquí es donde fallan la mayoría de los conversores en línea. Traducen exactamente lo que pegas y nada más, por lo que los tipos solo son tan correctos como tu única muestra.
Error 1: Campos opcionales vs obligatorios#
Una sola respuesta no puede indicarte si un campo es opcional. Si avatarUrl está presente en tu muestra pero la API lo omite para usuarios que nunca configuraron uno, un generador lo marca como obligatorio y tu código falla en tiempo de ejecución cuando falta.
Tienes dos opciones honestas. O alimentas al generador con múltiples muestras para que pueda ver qué claves desaparecen, o marcas los campos como opcionales manualmente según la documentación de la API:
interface User {
id: number;
username: string;
email: string;
isActive: boolean;
roles: string[];
profile: Profile;
lastLoginAt?: string; // opcional: ausente para usuarios que nunca iniciaron sesión
}
El ? hace que la propiedad sea string | undefined. Eso es diferente de string | null. null significa que la clave está presente con un valor nulo; undefined significa que la clave puede estar completamente ausente. Confundir ambos es una fuente clásica de errores de cannot read property of undefined.
Error 2: Tipos unión a partir de arrays mixtos#
Los arrays reales no siempre son homogéneos. Considera un feed de eventos:
{
"events": [
{ "type": "click", "x": 120, "y": 44 },
{ "type": "scroll", "delta": -300 }
]
}
Un generador débil o toma la forma del primer elemento e ignora el resto, o fusiona todos los campos en un solo objeto inflado con todo opcional. La salida correcta es una unión discriminada:
type AppEvent =
| { type: "click"; x: number; y: number }
| { type: "scroll"; delta: number };
interface EventPayload {
events: AppEvent[];
}
El campo compartido type es el discriminante. TypeScript ahora puede reducir la unión en un switch (event.type), algo que una interfaz fusionada nunca permitiría.
Error 3: Arrays vacíos y campos solo nulos#
¿Qué tipo es "tags": []? No hay ningún elemento del que inferir, por lo que la respuesta honesta es "desconocido". Las herramientas ingenuas emiten any[] o never[], ambos incorrectos en la práctica. El mismo problema afecta a un campo cuyo único valor de muestra es null: no puedes conocer el tipo no nulo solo a partir de null. Cuando te encuentres con estos, proporciona una muestra con datos o anota el tipo tú mismo (tags: string[]) según lo que sepas que devuelve la API.
Cuando También Necesitas Zod (Validación en Tiempo de Ejecución)#
Los tipos de TypeScript desaparecen en tiempo de compilación. No te brindan ninguna protección contra una API que realmente devuelve algo diferente a lo que tipaste. Si el servidor envía una cadena donde esperabas un número, TypeScript ya confió en la forma incorrecta y tu aplicación falla aguas abajo con un error confuso.
Zod cierra esa brecha. Defines un esquema una vez, validas la respuesta en el límite e infieres el tipo estático del mismo esquema para no escribirlo dos veces:
import { z } from "zod";
const ProfileSchema = z.object({
displayName: z.string(),
avatarUrl: z.string().url().nullable(),
});
const UserSchema = z.object({
id: z.number().int(),
username: z.string(),
email: z.string().email(),
isActive: z.boolean(),
roles: z.array(z.string()),
profile: ProfileSchema,
lastLoginAt: z.string().datetime().optional(),
});
// El tipo estático se deriva del esquema, no se escribe a mano:
type User = z.infer<typeof UserSchema>;
// En el límite de la red:
const user = UserSchema.parse(await response.json());
Ahora UserSchema.parse() lanza un error preciso y legible en el momento en que los datos violan el contrato, y z.infer mantiene el tipo TypeScript perfectamente sincronizado. Usa Zod (o un similar como Valibot) siempre que los datos crucen un límite de confianza: respuestas HTTP, entrada de formularios, webhooks, colas de mensajes, cualquier cosa que no hayas producido tú mismo. Para datos internos completamente tipados que controlas de principio a fin, las interfaces simples son suficientes.
Regla general: si el JSON proviene de fuera de tu código, valídalo en tiempo de ejecución. Los tipos solos son una promesa, no una garantía.
¿Necesitas una estructura de Go en su lugar?#
Los equipos de backend a menudo necesitan la misma forma en Go. El mapeo refleja TypeScript, con etiquetas de estructura que llevan los nombres de clave JSON:
type Profile struct {
DisplayName string `json:"displayName"`
AvatarURL *string `json:"avatarUrl"`
}
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
IsActive bool `json:"isActive"`
Roles []string `json:"roles"`
Profile Profile `json:"profile"`
LastLoginAt string `json:"lastLoginAt"`
}
Observa el puntero *string para avatarUrl: en Go, un puntero es como se representa un campo anulable, ya que el valor cero de string es "", no nil. La misma cuestión de nulabilidad del lado de TypeScript resurge aquí con una forma diferente.
Genérelo sin pegarlo en un sitio aleatorio#
Hacer esto manualmente para un payload grande es tedioso y propenso a errores, pero pegar JSON de producción, que puede contener tokens, correos electrónicos o datos de clientes, en un servicio remoto desconocido es peor. Un conversor que respeta la privacidad realiza la transformación completamente en tu navegador, por lo que los datos nunca tocan un servidor.
Paso 1: Pega y valida el JSON#
Coloca tu respuesta de API en un formateador y conversor de JSON gratuito. Primero valida el JSON y lo imprime de forma legible, para que detectes una coma final o una clave sin comillas antes siquiera de pensar en tipos. El JSON mal formado es la razón más común por la que una conversión produce silenciosamente basura.
Paso 2: Elige tu destino de salida#
Selecciona el formato que necesitas: una interface de TypeScript, un esquema de Zod o un struct de Go. Como el conversor de JSON a TypeScript se ejecuta del lado del cliente, puedes lanzarle respuestas reales sin censurar sin preocuparte por dónde van los datos. Toda la transformación ocurre en la pestaña.
Paso 3: Refina campos opcionales y uniones a mano#
La generación te da un esqueleto correcto a partir de la muestra. Ahora aplica los tres puntos clave: marca las claves realmente opcionales con ?, amplía los campos que solo contienen null o arrays vacíos a sus tipos reales, y convierte los arrays de objetos mixtos en uniones discriminadas. Este último 10% es un juicio que la herramienta no puede hacer a partir de un solo payload, y es lo que separa los tipos que compilan de los tipos que realmente coinciden con la API.
Cuando tu flujo de trabajo involucra más que tipos, el mismo conjunto de herramientas ayuda: valida tokens con las herramientas adyacentes al decodificador JWT, prueba patrones de extracción en el probador de expresiones regulares o maneja payloads codificados con el codificador y decodificador Base64. Para una mirada más profunda a los flujos de trabajo diarios con JSON, consulta nuestra guía sobre por qué un formateador de JSON es una herramienta diaria para desarrolladores.
Preguntas Frecuentes#
¿Cómo convierto JSON a una interfaz de TypeScript?
Mapea cada valor JSON a su equivalente en TypeScript: cadenas a string, números a number, objetos a interfaces anidadas y arreglos a T[]. Extrae las formas de objetos reutilizadas en interfaces con nombre. La ruta confiable más rápida es pegar el JSON en un conversor y luego corregir manualmente los campos opcionales, uniones nulas y arreglos mixtos que la muestra no pudo revelar.
¿Debería usar una interfaz o un alias de tipo para datos JSON?
Por defecto, usa interface para formas de objetos de una API, ya que las interfaces se extienden limpiamente y dan mensajes de error más claros. Usa un alias type cuando los datos sean en sí mismos una unión, intersección o tupla que una interfaz no pueda expresar, como un campo de estado que sea "pendiente" | "completado".
¿Cómo manejo campos opcionales al generar tipos desde JSON?
Una sola muestra no puede revelar campos opcionales, por lo que un generador marca todo como requerido. Aliméntalo con múltiples respuestas para que vea qué claves desaparecen, o agrega el modificador ? tú mismo basándote en la documentación de la API. Recuerda que campo?: string (puede estar ausente) difiere de campo: string | null (presente pero nulo).
¿Necesito Zod si ya tengo tipos de TypeScript?
Sí, siempre que los datos crucen un límite de confianza. Los tipos de TypeScript se borran en tiempo de compilación y no pueden validar lo que una API realmente devuelve en tiempo de ejecución. Zod valida la carga útil real y te permite inferir el tipo estático desde el mismo esquema con z.infer, por lo que el tipo y la verificación en tiempo de ejecución nunca se desvían.
¿Es seguro convertir JSON a TypeScript en línea? Solo si el conversor se ejecuta en tu navegador. Muchas herramientas en línea envían tu JSON pegado a un servidor, lo cual es riesgoso cuando la carga útil contiene tokens, correos electrónicos o datos de clientes. Un conversor del lado del cliente como el formateador JSON de Molixa realiza toda la transformación localmente, por lo que los datos nunca salen de tu pestaña.
¿Cómo convierto objetos JSON anidados a TypeScript?
Extrae cada objeto anidado en su propia interfaz con nombre y referénciala desde el padre, en lugar de incluir todo profundamente. Para el ejemplo de usuario, perfil se convierte en una interfaz Perfil separada que Usuario referencia. Las interfaces con nombre son reutilizables, producen mensajes de error más cortos y hacen que los tipos generados sean mucho más fáciles de leer.



