Question
I am reading a JSON object from a remote REST server. By design, the JSON contains all the properties expected by a TypeScript class. How can I cast the received JSON object to a typed variable?
I would prefer not to manually populate a TypeScript variable with a constructor that copies values from the JSON object, especially because the object is large and includes nested objects and many properties.
Short Answer
By the end of this page, you will understand the difference between a plain JSON object and a real TypeScript class instance. You will learn when a simple type assertion is enough, when it is misleading, and how to properly create class instances from JSON when you need class methods or runtime behavior.
Concept
TypeScript types exist mainly at compile time, not at runtime. That means when you receive JSON from a REST API, what you actually get is a plain JavaScript object, not an instance of your TypeScript class.
For example, if you define a class like this:
class User {
name: string;
getDisplayName() {
return this.name.toUpperCase();
}
}
and receive this JSON:
{ "name": "Ava" }
that JSON may have the same properties, but it is still not a User instance. It will not automatically have the class prototype, methods, getters, or constructor behavior.
This distinction matters because there are two different goals:
- Tell TypeScript what shape the object has
- Create a real class instance
A type assertion like this:
const user = json as User;
only tells the TypeScript compiler to treat the object as a User. It does not convert the object into a real User instance.
If your code only needs property access, this may be acceptable. If your code relies on class methods, validation logic, computed properties, or inheritance, you need to actually create an instance.
In real programming, this concept matters whenever you:
- consume API responses
- parse local storage data
- deserialize configuration files
- restore objects with methods after JSON parsing
The key idea is:
- JSON gives you data
- Classes give you behavior
Mental Model
Think of JSON as a cardboard cutout of an object, while a class instance is the real machine.
The cardboard cutout may look correct from the front:
- same property names
- same values
- same nested structure
But it does not contain the moving parts:
- methods
- prototype chain
- constructor setup
- runtime logic
So if you only need to read the labels on the cardboard, a plain object is fine. But if you want to press buttons and use the machine's behavior, you need to build a real instance.
Syntax and Examples
1. Type assertion for plain data
If you only need the object as structured data, you can use a type assertion:
class User {
id!: number;
name!: string;
}
const json = { id: 1, name: "Ava" };
const user = json as User;
console.log(user.name); // Ava
This works for property access in TypeScript, but user is still just a plain object.
2. Why methods do not work automatically
class User {
id!: number;
name!: string;
greet() {
return `Hello, ${this.name}`;
}
}
const json = { id: 1, name: "Ava" };
const user = json as User;
.(user.);
Step by Step Execution
Consider this example:
class Product {
id!: number;
title!: string;
label() {
return `#${this.id} ${this.title}`;
}
}
const json = { id: 10, title: "Keyboard" };
const product = Object.assign(new Product(), json);
console.log(product.label());
Step by step:
class Productdefines a class with two properties and one method.jsonis a plain JavaScript object received from somewhere such as an API.new Product()creates a real class instance.Object.assign(new Product(), json)copies the enumerable properties fromjsoninto that instance.- The result is an object that contains:
Real World Use Cases
This concept appears often in real applications:
API responses
A frontend fetches users, products, or orders from a REST API. The response is plain JSON. If the app only displays values, plain typed objects are often enough.
Domain models with behavior
If your class contains methods like:
getFullName()isExpired()calculateTotal()
then API JSON must be turned into real class instances before those methods can be used.
Local storage/session storage
When saving objects to storage, methods are lost because JSON only stores data. On restore, you may need to rebuild class instances.
Date handling
APIs often send dates as strings. Even if you assign the JSON to a class, a date field will still be a string unless you explicitly convert it.
Validation and business logic
Some projects keep logic inside classes. In those cases, deserialization is important so that runtime objects behave correctly.
Real Codebase Usage
In real codebases, developers usually choose one of these patterns:
1. Use interfaces for API data
If the response is only data, many teams use an interface or type instead of a class.
interface UserDto {
id: number;
name: string;
}
This is often the simplest option because API responses are plain objects anyway.
2. Map DTOs to domain models
A common pattern is:
- receive JSON as a DTO
- validate or transform it
- convert it to a class instance or richer model
interface UserDto {
id: number;
name: string;
}
class User {
constructor(public id: number, public name: string) {}
greet() {
return `Hello, ${this.name}`;
}
}
function toUser(): {
(dto., dto.);
}
Common Mistakes
Mistake 1: Thinking as MyClass creates an instance
Broken example:
class User {
name!: string;
greet() {
return `Hello ${this.name}`;
}
}
const json = { name: "Ava" };
const user = json as User;
user.greet();
Problem:
useris still a plain objectgreet()does not actually exist at runtime
Avoid it by creating an instance:
const user = Object.assign(new User(), json);
Mistake 2: Using classes when an interface is enough
If the object is only data, a class may add unnecessary complexity.
Better:
{
: ;
: ;
}
: = json;
Comparisons
| Approach | What it does | Best for | Limitation |
|---|---|---|---|
json as MyClass | Tells TypeScript to treat JSON as a type | Simple property access | Does not create a real instance |
const x: MyType = json | Assigns plain data to a typed variable | DTOs and API responses | No class behavior |
Object.assign(new MyClass(), json) | Copies data into a real instance | Simple classes with methods | Nested objects still need conversion |
| Manual mapping | Explicitly transforms fields | Validation and complex models | More code |
| Interface/type instead of class | Describes data shape only | Most API response models |
Cheat Sheet
// Plain JSON object from API
const json = await response.json();
// Type assertion: compile-time only
const user = json as User;
// Real instance for flat objects
const user = Object.assign(new User(), json);
// Nested conversion pattern
const user = Object.assign(new User(), {
...json,
address: Object.assign(new Address(), json.address)
});
Key rules:
as MyClassdoes not instantiate a class- JSON from APIs is a plain object
- Use an
interfaceortypeif you only need data
FAQ
Can I cast JSON directly to a TypeScript class?
You can use a type assertion, but it only affects TypeScript's static checking. It does not create a real class instance at runtime.
Why does as MyClass not give me class methods?
Because the original object is still a plain JavaScript object. It does not inherit from your class prototype.
What is the simplest way to convert JSON to a class instance?
For simple flat objects, use:
const obj = Object.assign(new MyClass(), json);
Should I use a class or an interface for API responses?
If the response is just data, an interface or type is usually the better choice. Use a class when you need methods or domain behavior.
Does Object.assign handle nested objects automatically?
No. It only copies top-level properties. Nested objects remain plain objects unless you convert them too.
What about arrays of objects?
Map each item:
const users = jsonArray.map(item => Object.assign(new User(), item));
Is manual mapping ever better?
Mini Project
Description
Build a small example that loads plain API-style user data and converts it into real TypeScript class instances. This project demonstrates the difference between plain objects and class instances, including how to handle nested objects and how to use class methods safely.
Goal
Create a user model with a nested address model, convert JSON data into class instances, and call methods on the resulting objects.
Requirements
- Create an
Addressclass with a method that returns a formatted location string. - Create a
Userclass with a method that returns a readable profile string. - Start with plain JSON data that includes a nested address object.
- Convert the JSON into real
UserandAddressinstances. - Print output using class methods to prove the conversion worked.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular formControl Error with Material Autocomplete: Why It Happens and How to Fix It
Learn why Angular says it cannot bind to formControl and how to fix Reactive Forms setup with Angular Material Autocomplete.