Как преобразовать JSON-объект в экземпляр класса TypeScript?
Проблема приведения типов
Вы абсолютно правы: оператор as в TypeScript — это всего лишь утверждение типа для компилятора. Он не создаёт настоящий экземпляр класса, а только "говорит" TypeScript, что объект имеет такую форму. Методы класса при этом не копируются. JSON.parse() возвращает обычный объект без прототипа класса.
Правильное решение: ручное создание экземпляра
Самый надёжный способ — написать функцию или метод, который принимает сырой JSON и возвращает настоящий экземпляр класса:
class User {
name: string;
age: number;
email: string;
constructor(name: string, age: number, email: string) {
this.name = name;
this.age = age;
this.email = email;
}
static fromJSON(json: any): User {
return new User(json.name, json.age, json.email);
}
isAdult(): boolean {
return this.age >= 18;
}
}
// Использование:
const response = await fetch('/api/user');
const jsonData = await response.json();
const user = User.fromJSON(jsonData);
console.log(user.isAdult()); // true
Альтернатива: конструктор с объектом
Можно сделать конструктор, принимающий частичный объект:
class User {
name: string;
age: number;
email: string;
constructor(data: Partial<User>) {
Object.assign(this, {
name: '',
age: 0,
email: '',
...data
});
}
isAdult(): boolean {
return this.age >= 18;
}
static fromJSON(json: any): User {
return new User(json);
}
}
Использование библиотеки class-transformer
Для сложных проектов рекомендую библиотеку class-transformer от TypeStack:
import { plainToClass } from 'class-transformer';
class User {
@Expose()
name: string;
@Expose()
age: number;
@Expose()
email: string;
get isAdult(): boolean {
return this.age >= 18;
}
}
// Использование:
const jsonData = { name: 'Иван', age: 25, email: 'ivan@example.com' };
const user = plainToClass(User, jsonData);
console.log(user.isAdult); // true
Важные моменты:
- Всегда проверяйте данные — JSON может содержать невалидные значения. Используйте валидацию (например, class-validator).
- Для вложенных объектов — рекурсивно создавайте экземпляры всех вложенных классов.
- Используйте интерфейсы для типизации JSON — это отделяет "сырые" данные от бизнес-логики.
Почему это работает?
Когда вы вызываете new User(), JavaScript создаёт новый объект с прототипом User.prototype. Именно в прототипе хранятся методы класса. Простое приведение типа через as не меняет прототип объекта, поэтому методы недоступны.
Рекомендация для начинающих
Начните с простого статического метода fromJSON. Он понятен, не требует дополнительных зависимостей и даёт полный контроль над процессом преобразования. По мере роста проекта переходите на class-transformer — он автоматизирует рутину.
Альтернативный подход: интерфейсы + функции-конструкторы
Многие разработчики предпочитают разделять данные и логику. Вместо класса используйте интерфейс для описания структуры данных и обычную функцию для валидации/преобразования:
interface UserData {
name: string;
age: number;
email: string;
}
function createUser(data: UserData): UserData & { isAdult(): boolean } {
if (!data.name || data.age < 0) {
throw new Error('Invalid user data');
}
return {
...data,
isAdult(): boolean {
return this.age >= 18;
}
};
}
// Использование:
const jsonData = await fetch('/api/user').then(r => r.json());
const user = createUser(jsonData);
console.log(user.isAdult()); // true
Плюсы этого подхода:
- Нет привязки к классам — проще тестировать и рефакторить.
- Явная валидация — функция проверяет данные перед созданием объекта.
- Гибкость — можно легко добавить вычисляемые свойства или методы.
Когда это уместно?
Если ваш проект не требует сложной иерархии классов или наследования, этот подход часто оказывается проще и чище. Он особенно хорош для API-слоя, где данные приходят с сервера и не должны содержать бизнес-логику.
Не забывайте, что в TypeScript главное — типизация, а не обязательно классы. Интерфейсы + чистые функции — это мощная идиома функционального программирования, которая отлично работает с JSON.
Ответов пока нет.
Войдите чтобы оставить ответ