Как преобразовать JSON объект в экземпляр класса TypeScript?
Проблема: JSON.parse() создаёт обычные объекты, а не экземпляры класса
Когда вы используете JSON.parse(), вы получаете простой JavaScript объект, который не является экземпляром вашего класса. TypeScript только проверяет типы на этапе компиляции, но в runtime это обычный объект без методов класса. Вот несколько проверенных способов решения этой проблемы.
Способ 1: Фабричный метод (рекомендуемый)
Самый чистый подход — добавить статический метод в класс, который принимает JSON объект и возвращает экземпляр класса:
class User {
constructor(
public id: number,
public name: string,
public email: string,
public createdAt: Date
) {}
// Фабричный метод для создания из JSON
static fromJSON(json: any): User {
return new User(
json.id,
json.name,
json.email,
new Date(json.createdAt)
);
}
getDisplayName(): string {
return `${this.name} (${this.email})`;
}
}
// Использование
const jsonData = '{"id": 1, "name": "Иван", "email": "ivan@example.com", "createdAt": "2024-01-15T10:30:00Z"}';
const parsedData = JSON.parse(jsonData);
const user = User.fromJSON(parsedData);
console.log(user.getDisplayName()); // "Иван (ivan@example.com)"
console.log(user instanceof User); // true
Способ 2: Конструктор с Object.assign()
Можно использовать конструктор, который копирует свойства из обычного объекта:
class User {
constructor(data: Partial<User>) {
Object.assign(this, data);
// Преобразуем строку в Date, если нужно
if (typeof this.createdAt === 'string') {
this.createdAt = new Date(this.createdAt);
}
}
getDisplayName(): string {
return `${this.name} (${this.email})`;
}
}
// Использование
const jsonData = '{"id": 1, "name": "Иван", "email": "ivan@example.com", "createdAt": "2024-01-15T10:30:00Z"}';
const user = new User(JSON.parse(jsonData));
console.log(user.getDisplayName()); // "Иван (ivan@example.com)"
Способ 3: Использование библиотеки class-transformer (для сложных случаев)
Если у вас много вложенных объектов или сложные преобразования, используйте библиотеку class-transformer:
import { plainToClass } from 'class-transformer';
class User {
id: number;
name: string;
email: string;
createdAt: Date;
getDisplayName(): string {
return `${this.name} (${this.email})`;
}
}
const jsonData = '{"id": 1, "name": "Иван", "email": "ivan@example.com", "createdAt": "2024-01-15T10:30:00Z"}';
const user = plainToClass(User, JSON.parse(jsonData));
console.log(user.getDisplayName()); // "Иван (ivan@example.com)"
console.log(user instanceof User); // true
Важные моменты:
- Всегда проверяйте типы — JSON может содержать невалидные данные. Используйте валидацию (например, библиотеку
class-validator). - Обрабатывайте вложенные объекты — если у вас есть массивы или вложенные объекты, их тоже нужно преобразовывать рекурсивно.
- Преобразуйте типы — даты, числа и другие специальные типы нужно явно преобразовывать из строк.
Альтернативный подход: Использование интерфейсов вместо классов
Если вам не нужны методы класса, а только структура данных, рассмотрите использование интерфейсов вместо классов. Это проще и не требует преобразования:
interface IUser {
id: number;
name: string;
email: string;
createdAt: string; // или Date
}
// Функция для работы с данными
function getDisplayName(user: IUser): string {
return `${user.name} (${user.email})`;
}
const jsonData = '{"id": 1, "name": "Иван", "email": "ivan@example.com", "createdAt": "2024-01-15T10:30:00Z"}';
const user: IUser = JSON.parse(jsonData);
console.log(getDisplayName(user)); // "Иван (ivan@example.com)"
Этот подход особенно полезен, когда данные приходят из внешних источников и не требуют сложной бизнес-логики. Вы также можете комбинировать интерфейсы с классами-помощниками для валидации и преобразования данных.