JSON a TypeScript
Pega JSON y obtén interfaces TypeScript al instante.
Cómo usar
- Pega tu JSON en el panel de la izquierda (objeto o array).
- Haz clic en Convertir · las interfaces TypeScript aparecen a la derecha.
- Ajusta el nombre raíz y activa las opciones según necesites.
- Haz clic en Copiar la salida para copiar el código generado.
Preguntas frecuentes
¿Gestiona los objetos anidados?
Sí. Cada objeto anidado recibe su propia interfaz con nombre. Los elementos de los arrays también se analizan para determinar el tipo de elemento.
¿Qué pasa con arrays de tipos mixtos?
Si un array contiene elementos de tipos distintos, el convertidor usa un tipo unión (p. ej. string | number).
¿Admite arrays JSON en la raíz?
Sí. Si el valor raíz es un array, el convertidor analiza sus elementos y produce la interfaz apropiada así como un alias de tipo para el array.
Qué hace realmente el convertidor
Analiza tu JSON, recorre cada valor y emite un conjunto de declaraciones interface de TypeScript que describen la forma. Cada objeto anidado recibe su propia interfaz con nombre, derivada de la clave de propiedad del padre en PascalCase (un campo user se convierte en una interfaz User). Cuando dos objetos anidados chocan en el nombre, el segundo recibe un sufijo numérico (User, User2). En la raíz, un array se convierte en un alias type Root = RootItem[]; más la interfaz RootItem; un objeto se convierte en una única interfaz Root (o el nombre que pongas en el campo «nombre raíz»).
Ejemplo de entrada y salida:
// Input
{
"id": 42,
"name": "Ada",
"active": true,
"tags": ["admin", "billing"],
"profile": { "bio": "Programmer", "link": null }
}
// Output
export interface Root {
id: number;
name: string;
active: boolean;
tags: string[];
profile: Profile;
}
export interface Profile {
bio: string;
link: unknown;
}
La correspondencia de tipos JSON → TypeScript
| Valor JSON | TS generado |
|---|---|
"hello" | string |
42 o 3.14 | number (TS no tiene un tipo entero aparte) |
true / false | boolean |
null | unknown (el convertidor no puede saber si el campo admite valores nulos o simplemente está ausente en esta muestra) |
[] | unknown[] |
[1, 2, 3] | number[] |
[1, "x"] | (number | string)[] |
{ "a": 1 } | interface con nombre |
Para los arrays de objetos de nivel raíz, el convertidor fusiona las claves de todos los elementos en una única interfaz representativa. Es una aproximación razonable cuando los elementos comparten una forma, pero pierde información cuando las formas divergen de verdad (p. ej. un array de eventos heterogéneo). Para ese caso, tendrías que escribir a mano una unión discriminada después de la generación.
Por qué interface para los objetos y type para el array raíz
El Handbook oficial de TypeScript da una heurística de una línea: «Si quieres una heurística, usa interface hasta que necesites usar características de type.» Ambos están tipados estructuralmente y ambos pueden describir formas de objeto, pero interface gana para las formas de objeto porque admite la fusión de declaraciones (reabrir la interfaz para añadir campos), funciona limpiamente con extends y aparece por su nombre en los errores del compilador. Los alias type son obligatorios cuando necesitas uniones de primitivos, intersecciones de varios tipos o nombrar algo que no es un objeto, razón por la cual el array de nivel raíz recibe un alias type Root = RootItem[]; en lugar de una interfaz.
Una nota sobre los nombres: el convertidor usa PascalCase sin el prefijo heredado I (así que User en lugar de IUser). Las guías de estilo modernas de TS (la de Google, la de Microsoft, la de la comunidad de React) prescinden del prefijo casi universalmente. Los nombres de propiedad coinciden exactamente con las claves JSON (camelCase si el JSON usa camelCase, snake_case si usa snake_case). Las claves que no son identificadores JS válidos (que contienen guiones, espacios o que empiezan por un dígito) van entre comillas: "foo-bar": string;.
Lo que esta herramienta no hace (seamos honestos sobre los límites)
Un único ejemplo JSON contiene menos información que un esquema real, así que varias cosas que estaría bien detectar no son posibles solo a partir de la entrada:
- Campos opcionales. Cada propiedad de tu ejemplo se vuelve obligatoria. Si la API real a veces omite un campo, tienes que añadir el
?a mano:email?: string;. O ejecuta el convertidor sobre varios ejemplos y une los resultados. - Detección de nulabilidad. Un
nullen el ejemplo se convierte enunknown; el convertidor no puede saber si el campo admite nulos de verdad (string | null) o si simplemente resultó ser nulo en esta muestra. Ajústalo a mano según el contrato de la API. - Uniones de literales de cadena. Un campo de estado con el valor
"active"se convierte enstring, no en"active" | "inactive" | "pending". El convertidor no puede ver los demás valores. - Detección del tipo Date. Una cadena ISO 8601 como
"2024-04-12T10:00:00Z"se queda comostring. ElDatede JavaScript no es un tipo JSON. - Uniones discriminadas. Si un array raíz contiene elementos de formas distintas (eventos de tipos diferentes, registros polimórficos), el convertidor fusiona todas las claves en una sola interfaz en lugar de producir una unión etiquetada. Para datos heterogéneos de verdad, querrás escribir la unión a mano.
- Precisión numérica. El
numberde JavaScript representa de forma segura los enteros hasta 253−1. Los IDs enteros más grandes pierden precisión cuando los analizaJSON.parse; si tu API devuelve enteros de 64 bits, el patrón más seguro es enviarlos como cadenas y tiparlos comostring.
Cuándo usar esta herramienta
- Generar tipos iniciales a partir de una respuesta de API real. Llama al endpoint, pega el JSON y obtén un conjunto inicial de interfaces. Ajústalo a mano para los campos opcionales y los literales de cadena.
- Añadir tipos a un SDK de terceros sin tipos. Muchas bibliotecas JS antiguas se distribuyen sin tipos. Generar a partir de una respuesta de muestra es más rápido que leer la documentación.
- Compartir tipos entre el front-end y el back-end. Genera a partir de un ejemplo canónico y cópialos en ambas bases de código (o compártelos mediante un paquete de tipos publicado).
- Migrar una base de código JS a TypeScript. Genera tipos a partir de los datos reales que fluyen por la aplicación y anota gradualmente las firmas de función.
- Generar tipos para un archivo de configuración. El tipado estricto hace visibles los errores de configuración en tiempo de compilación.
- Documentar la forma de un archivo de datos interno. La interfaz generada hace las veces de documentación legible por máquina.
Primero el código, primero el esquema o directo (dónde encaja esta herramienta)
Tres flujos de trabajo habituales para obtener tipos de TypeScript a partir de datos en tiempo de ejecución:
- Validadores en tiempo de ejecución (code-first): Zod, Yup, Effect Schema, Joi. Escribes un esquema de validación en JS y te da a la vez el análisis en tiempo de ejecución y un tipo de TypeScript por inferencia. El esquema es la fuente de la verdad. Mejor cuando la validación importa tanto como el tipo.
- Primero el esquema (schema-first): escribe un JSON Schema o una especificación OpenAPI y genera los tipos de TypeScript con herramientas como
quicktype,json-schema-to-typescriptuopenapi-typescript. Mejor cuando un contrato de API abarca varios lenguajes. - Inferencia directa a partir de la muestra (esta herramienta, más el modo JSON plano de quicktype): pega datos reales y obtén tipos. El camino más rápido; las garantías de validación más débiles, porque nada comprueba los datos en tiempo de ejecución frente al tipo.
La elección correcta depende de si tu preocupación es sobre todo la comprobación estática de tipos (esta herramienta es estupenda) o también la seguridad en tiempo de ejecución (recurre a Zod / Effect Schema en su lugar).
Errores comunes
- Tratar los tipos generados como una especificación terminada. Son un punto de partida hecho al 70 %. Añade
?para los campos opcionales,| nulldonde realmente admita nulos y uniones de literales de cadena donde corresponda. - Generar a partir de un único ejemplo cuando la API tiene variantes. Un objeto de usuario que a veces tiene un
emaily a veces no se generará como «email es obligatorio». Ejecuta varios ejemplos y une los resultados, o edítalo a mano. - Marcar todo como
readonlyautomáticamente. Es útil para los flujos de datos inmutables, pero está mal para los objetos de estado que mutas. Usa la opciónreadonlycon criterio. - Confiar en la precisión con IDs enteros grandes. El
numberde JavaScript llega como máximo a 253−1. Para los IDs de 64 bits, transmítelos como cadenas y típalos comostring. - Confundir
interfacecontype. Herramientas distintas, compensaciones distintas. La heurística del Handbook de TypeScript es «usa interface a menos que necesites una característica que solo type ofrece,» exactamente lo que hace esta herramienta. - Pegar JSON confidencial en un convertidor del lado del servidor. Las respuestas de API reales suelen contener datos de clientes, credenciales e IDs internos. La conversión solo en el navegador (esta herramienta) mantiene los datos en tu máquina.
Más preguntas frecuentes
¿Por qué se marcaron todos mis campos como obligatorios?
Porque un único ejemplo JSON no puede decirle al convertidor qué campos faltan a veces en la API real. Cada clave que aparece en el ejemplo se vuelve obligatoria. Si sabes que un campo es opcional, añade un ? final a mano: email?: string;. Para la detección de opcionalidad con varias muestras, la CLI más sofisticada de quicktype lo gestiona de forma nativa.
¿Por qué mi campo nulo está tipado como unknown?
Porque el convertidor no puede saber, a partir de un único nulo, si el campo siempre admite nulos (string | null) o si simplemente resultó ser nulo en esta muestra (string). unknown es el valor por defecto conservador; TypeScript te obligará a acotar el tipo antes de usar el valor, lo que es más seguro que any. Edítalo a string | null a mano cuando conozcas la intención de la API.
¿Debería usar interface o type?
La heurística del Handbook oficial de TypeScript: usa interface hasta que necesites una característica que solo proporciona type, principalmente los tipos unión, los tipos intersección o nombrar un primitivo. Este convertidor sigue esa regla: interface para cada forma de objeto y type para el alias del array raíz. Algunas guías de estilo recomiendan usar type por defecto en su lugar (Matt Pocock ha defendido públicamente esa postura), pero el valor por defecto convencional es interface para los objetos.
¿Se subirá mi JSON a algún sitio?
No. La conversión es una IIFE de JavaScript autónoma que analiza tu JSON con JSON.parse, recorre el valor y escribe la salida de TypeScript en un área de texto. No hay ningún fetch, ninguna llamada de analítica que transporte el contenido del JSON, ningún servidor. Esto importa porque las respuestas de API reales suelen contener datos de clientes, IDs internos o credenciales que no quieres que circulen por un tercero.
¿Qué pasa con JSDoc, Zod o la validación en tiempo de ejecución?
Esta herramienta emite solo tipos de TypeScript en tiempo de compilación: sin validadores en tiempo de ejecución (sin Zod / Yup / Effect Schema), sin comentarios JSDoc. Si necesitas una validación en tiempo de ejecución que sirva a la vez como tipo estático, escribe el esquema en Zod y usa el z.infer<typeof Schema> de Zod para derivar el tipo. Si tienes un JSON Schema y quieres a la vez validación en tiempo de ejecución y tipos de TS, la combinación habitual es json-schema-to-typescript + AJV.
¿Por qué no hay un prefijo I en los nombres de las interfaces?
Porque la mayoría de las guías de estilo modernas de TypeScript prescinden de él. La Guía de estilo de TypeScript de Google lo desaconseja explícitamente; la comunidad de React ha convergido en PascalCase sin prefijo. La convención heredada IUser vino de la influencia de C# / Java en los primeros tiempos de TypeScript; la práctica actual es User a secas.