online json to typescript interface converter & ast generator

typetools

instant, client-side type converter • 0ms network latency

root:
inputjson (rfc 8259)
paste or drop file
277 chars
outputtypescript
*.ts
export interface UserMetadata {
  lastLogin: string;
  ipAddress: string;
  failedAttempts: null | number;
}

export interface UserPayload {
  userId: string;
  username: string;
  email: string;
  isActive: boolean;
  score: number;
  roles: string[];
  metadata: UserMetadata;
}
0.15ms•in-browser
documentation, edge cases & specifications below

architecture & type system comparison

specification breakdown and memory characteristics

specification foundations & design rationale

The JavaScript Object Notation (JSON) format emerged from Douglas Crockford's formalization in the early 2000s, codified under ECMA-404 and IETF RFC 8259. JSON intentionally lacks type declarations, relying entirely on runtime key-value serialization. When Anders Hejlsberg led the creation of TypeScript at Microsoft in 2012, the central architectural challenge was typing dynamic, untyped JavaScript data without runtime performance penalties. Unlike nominal type systems (e.g., Java or C#) where two classes with identical fields are distinct types unless explicitly linked by inheritance, TypeScript employs a structural type system (often described as compile-time duck typing). Two types are assignable if their public shapes are compatible. Converting untyped JSON to TypeScript interfaces translates runtime dictionary structures directly into the compiler's Abstract Syntax Tree (AST), creating ts.InterfaceDeclaration and ts.PropertySignature nodes that disappear during emission, resulting in zero additional JavaScript bytes in your production bundle.

memory layout & runtime overhead

TypeScript interfaces exist purely in memory during the compilation phase within tsserver and tsc. Once compilation completes, the TypeScript AST is transformed by erasing all interface declarations, type aliases, and type annotations from the emitted JavaScript output. Consequently, the runtime memory overhead of a TypeScript interface is exactly 0 bytes. However, this compile-time erasure creates a critical boundary limitation: TypeScript cannot validate whether incoming HTTP JSON payloads actually conform to the declared interface at runtime. If an API returns null for a field typed as string, TypeScript will not intercept the violation until downstream code attempts an invalid method call (such as .toUpperCase()), resulting in an unhandled TypeError. Pairing interfaces with compile-time analyzers and schema boundaries is vital for enterprise stability.

compiler ast tokenization & node synthesis

The transformation pipeline begins with recursive lexical tokenization of the JSON text stream. The token stream is converted into an in-memory Document Object Model (DOM) tree. Next, the AST synthesis engine walks the tree using a depth-first traversal strategy: primitive scalars are mapped to TypeScript KeywordTypeNodes (ts.SyntaxKind.StringKeyword, NumberKeyword, BooleanKeyword), while compound objects trigger the creation of nested ts.InterfaceDeclaration nodes. Array values undergo member union folding: all array elements are analyzed to construct a unified type set. If conflicting types are detected (e.g., strings and numbers), an explicit ts.UnionTypeNode is synthesized. Finally, the constructed AST is formatted using TypeScript's official ts.Printer API, ensuring idiomatic indentation, semicolons, and export modifiers.

trade-off analysis & schema extensibility

Interfaces vs. Type Aliases: Interfaces support declaration merging (multiple interface definitions with the same identifier in different files will concatenate their fields), making them ideal for extensible SDKs and public library types. Interfaces also leverage internal compiler caching in tsserver, yielding faster type-checking in large codebases. In contrast, type aliases (type T = ...) are required for union representations (type Status = 'pending' | 'success'), primitive wrappers, tuple declarations, and mapped types. For plain deserialized JSON API responses, interfaces are recommended for root record definitions.

specification matrix (JSON (RFC 8259) vs. TypeScript Interface (AST ts.InterfaceDeclaration))

metricJSON (RFC 8259)TypeScript Interface (AST ts.InterfaceDeclaration)
Validation TimingRuntime (untyped object parsing)Compile-Time (100% erased during build)
Bundle Size Impact0 KB overhead0 KB (transpiled away by tsc/esbuild)
Type ParadigmDynamic weakly-typed schemaStatic structural subtyping
Ecosystem ToolingJSON Schema, RFC 8259Language Server Protocol (tsserver), VS Code intellisense
Execution CostJSON.parse CPU costZero runtime overhead

handling api edge cases & compiler gotchas

common deserialization pitfalls and solutions

Heterogeneous Array Unions

When JSON arrays contain mixed element types (e.g., [1, "apple", null]), the converter infers a union type Array<number | string | null>. In strict codebases, verify if the array is intended to be a tuple [number, string, null?] or an array of discriminated polymorphic objects.

// Heterogeneous JSON array:
["alpha", 42, null]

// Synthesized TypeScript AST:
type InferredArray = Array<string | number | null>;

Null vs Undefined Optionality

JSON specifies null but has no native representation for undefined. A field with key: null translates to key: string | null. However, if a key is absent across sibling payloads, it should be marked optional (key?: string). With TypeScript's exactOptionalPropertyTypes enabled, key?: string does not accept explicit null.

// Strict Optional Property vs Nullable:
interface StrictContract {
  missingField?: string;       // Accepts undefined, rejects null
  nullableField: string | null; // Requires key to exist, accepts null
}

64-bit Integer IEEE 754 Precision Loss

Standard JavaScript Number uses IEEE 754 double-precision floating-point, with an integer fidelity ceiling at Number.MAX_SAFE_INTEGER (9,007,199,254,740,991). High-precision numeric IDs (such as Twitter Snowflake IDs or database bigint columns) risk silent truncation if deserialized as number instead of string or bigint.

// Dangerous numeric ID in JSON:
{"snowflakeId": 18446744073709551615}

// Recommended Type Annotation:
snowflakeId: string; // or bigint with lossless JSON parser

Reserved Words & Escaped Identifiers

JSON keys may include hyphens, spaces, or ECMAScript reserved keywords (e.g., "user-id", "class", "default"). The generator safely wraps these properties in quotes ('user-id': string) to maintain valid TypeScript syntax.

export interface ApiPayload {
  'content-type': string;
  'class': string;
  'default': boolean;
}

step-by-step integration guide

technical specification differences & end-to-end conversion workflow

1Lexical Analysis & Schema Inference

The parser tokenizes the JSON payload into an in-memory object tree, resolving nested objects, arrays, and primitive leaf nodes.

npm install -D typescript @types/node
import * as ts from 'typescript';
const parsedTree = JSON.parse(rawJsonString);

2Type Normalization & Union Merging

Array elements are analyzed across all items to infer the broadest common type or synthesize a union type when types diverge.

type InferredMember = string | number | boolean | null;

3TypeScript AST Factory Synthesis

Uses TypeScript's compiler API factory methods (ts.factory.createInterfaceDeclaration) to emit strongly typed AST signatures.

ts.factory.createPropertySignature(
  undefined,
  ts.factory.createIdentifier("id"),
  undefined,
  ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
);

4Code Formatting & Export Emission

The AST is rendered via ts.createPrinter() into formatted, idiomatically indented TypeScript code ready for import.

const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
ci/cd automation command:
# Run TypeScript compiler verification on generated interfaces
npx tsc --noEmit --strict src/types/api-contracts.ts

frequently asked questions

common developer questions answered

q:Should I use 'interface' or 'type' for deserialized JSON API payloads?

For external JSON responses, 'interface' is preferred because interfaces support declaration merging, extend syntax for inheritance, and produce slightly faster compilation cycles in tsserver. However, 'type' aliases are mandatory when working with direct union outputs (e.g., type ApiResponse = SuccessResponse | ErrorResponse) or mapped types.

// Interface declaration merging example:
interface UserResponse {
  id: string;
}
interface UserResponse {
  roles: string[]; // Merged automatically into { id: string; roles: string[]; }
}

q:How does the converter handle nested polymorphic objects in JSON arrays?

When an array contains objects with varying fields, our AST inference engine computes the set union of all detected keys. Fields present in only a subset of items are automatically tagged with an optional modifier (?), while divergent field types are converted into union types (e.g., status: 'active' | number).

// Polled multi-item array inference:
const items = [{ id: 1, tag: 'prod' }, { id: 2, meta: { active: true } }];
// Yields interface with tag?: string; meta?: { active: boolean };

q:Why does JSON numeric parsing default to 'number' instead of 'bigint'?

JSON standard RFC 8259 does not specify numeric bit widths, but standard JavaScript JSON.parse parses all numbers as IEEE 754 floats. Emitting 'bigint' requires custom revivers or lossless JSON parsers. If your JSON contains 64-bit integer strings, consider annotating those specific fields as bigint or string manually.

// Using custom JSON reviver for bigints:
JSON.parse(text, (key, value) => {
  if (typeof value === 'string' && /^\d{16,}$/.test(value)) return BigInt(value);
  return value;
});
topic cluster

related type & ast converters

3 interconnected tools