JSON to TypeScript Class vs Interface — Which to Generate
- Interface is almost always right for JSON-derived shapes — zero runtime cost, same compile-time safety.
- Class earns its keep when you need methods, constructors, or decorator-based validation (NestJS).
- Generating a class from JSON usually just means generating an interface and wrapping it.
Table of Contents
Plenty of people search for "JSON to TypeScript class" and end up with an interface generator. For almost every use case that's the right outcome — interfaces are lighter, compile away to nothing, and do the same job for data shapes. Here's when a class is actually the right tool and how to turn an interface into one.
Interface Is Usually the Answer
For plain data shapes — API responses, configuration objects, form payloads, DTOs — an interface gives you:
- Compile-time type checking
- Autocomplete in editors
- Rename support across the codebase
- Zero runtime cost (interfaces erase at compile time)
Classes add:
- Methods attached to the shape
- A constructor you can invoke with
new - Runtime presence (a class is still a JS class after compile)
- Support for
instanceofchecks - Decorator-based metadata (NestJS, class-validator, TypeORM)
For JSON-derived shapes, you rarely need any of the extras. The data came in as JSON; it has no methods attached. Wrapping it in a class just for type safety is overhead.
When a Class Earns Its Keep
Three legitimate reasons to generate a class instead of an interface:
1. Decorator-based validation (NestJS, class-validator). NestJS's request validation system reads decorators like @IsEmail() and @MinLength() off class fields. Interfaces can't carry decorators. If you're building a NestJS API, DTOs are classes.
2. ORM models (TypeORM, MikroORM). Entity classes with @Entity(), @Column(), and relationship decorators. Again, class-only.
3. Genuine methods on the shape. A User class with fullName(), isAdmin(), or other computed accessors that travel with the data.
If none of those apply, interface is the right call. Check these three cases first before reaching for a class.
Sell Custom Apparel — We Handle Printing & Free ShippingGenerating Interface First, Wrapping as Class
Our tool generates interfaces. If you need a class, generate the interface and wrap it:
// Generated:
interface User {
id: number;
email: string;
displayName: string;
}
// Wrapped as class:
class User {
id: number;
email: string;
displayName: string;
constructor(data: User) {
this.id = data.id;
this.email = data.email;
this.displayName = data.displayName;
}
get initials(): string {
return this.displayName.split(' ').map(n => n[0]).join('');
}
}
Note the interface and class can coexist with the same name — TypeScript's declaration merging means new User(data) works and user: User types correctly.
For NestJS DTOs specifically, skip the interface middle step and declare the class directly with decorators. The interface won't add anything.
Runtime Behavior Differences
This is the part people miss. An interface is compile-time only:
// TypeScript:
interface User { id: number; name: string; }
const u: User = { id: 1, name: 'x' };
// After compilation, the interface is gone:
const u = { id: 1, name: 'x' };
A class stays in the runtime:
class User { id = 0; name = ''; }
const u = new User();
// After compilation:
class User { constructor() { this.id = 0; this.name = ''; } }
const u = new User();
Real consequence: you can do u instanceof User with a class. You cannot with an interface. If your code path depends on distinguishing "is this really a User" at runtime, you need a class.
For JSON that's parsed from an API response — const u = response.data; — that's a plain object, not an instance of any class. u instanceof User is false even if it matches the shape. Class-based type guards need an explicit rehydration step.
The Full Picture — Type vs Interface vs Class
| Feature | type alias | interface | class |
|---|---|---|---|
| Describes a shape | Yes | Yes | Yes |
| Runtime presence | No | No | Yes |
| Can be instantiated | No | No | Yes (new) |
| Supports methods | In object type | In object type | Yes, with this |
| Supports decorators | No | No | Yes |
| Supports union types | Yes | No | No |
| Declaration merging | No | Yes | No |
instanceof checks | No | No | Yes |
The practical decision tree: need decorators? class. Need runtime identity? class. Need a union? type. Everything else: interface.
Generate Your Interface
Paste JSON, get a TypeScript interface. Wrap as a class only when you need one.
Open Free JSON to TypeScript GeneratorFrequently Asked Questions
Does your tool generate classes?
No, interfaces only. Wrap the interface in a class if you need one — it is a ~10-line addition once you have the field list.
Why don't interfaces show up as a class instance?
Interfaces compile away. A plain object matching an interface is not "an instance" of anything — you need a class for instanceof checks.
Is there a performance difference between class and interface?
Interfaces have zero runtime cost. Classes have method lookup overhead on access. For most apps the difference is irrelevant; for hot paths interfaces win.
Can I use class-validator decorators with an interface?
No. Decorators need a class. For NestJS validation DTOs, declare the class directly with decorators on each field.

