Введение в работу с JSON в TypeScript
JSON (JavaScript Object Notation) остаётся одним из самых популярных форматов для обмена данными между клиентом и сервером. В TypeScript, который является строго типизированной надстройкой над JavaScript, работа с JSON требует особого внимания к типам данных. В этой статье мы подробно разберём, как правильно парсить и сериализовать JSON в TypeScript, избегая типичных ошибок и используя преимущества статической типизации.
TypeScript предоставляет все встроенные методы JavaScript для работы с JSON, но добавляет к ним мощную систему типов. Это позволяет создавать более надёжный и предсказуемый код. Мы рассмотрим как базовые операции, так и продвинутые техники, включая кастомные функции-валидаторы и работу с дженериками.
Основы парсинга JSON: JSON.parse()
Метод JSON.parse() преобразует строку JSON в объект JavaScript. В TypeScript важно правильно типизировать результат, чтобы компилятор мог проверять корректность обращений к свойствам объекта.
Базовый синтаксис и типизация
Рассмотрим простой пример:
const jsonString = '{"name": "Анна", "age": 30, "city": "Москва"}';
const user = JSON.parse(jsonString);
console.log(user.name); // Анна
Проблема в том, что TypeScript не знает структуру объекта user. Переменная имеет тип any, что лишает нас всех преимуществ типизации. Правильное решение — явно указать тип:
interface User {
name: string;
age: number;
city: string;
}
const jsonString = '{"name": "Анна", "age": 30, "city": "Москва"}';
const user: User = JSON.parse(jsonString); // Ошибка!
// Type 'any' is not assignable to type 'User'.
TypeScript выдаст ошибку, потому что JSON.parse() возвращает any, а мы пытаемся присвоить его типизированной переменной. Используйте приведение типа:
const user = JSON.parse(jsonString) as User;
console.log(user.name.toUpperCase()); // АННА
Безопасный парсинг с проверкой типов
Приведение типа as — это быстрое решение, но оно не гарантирует, что данные действительно соответствуют ожидаемой структуре. Для production-кода рекомендуется создавать функции-валидаторы:
function parseUser(data: string): User | null {
try {
const parsed = JSON.parse(data);
if (typeof parsed === 'object' && parsed !== null &&
typeof parsed.name === 'string' &&
typeof parsed.age === 'number' &&
typeof parsed.city === 'string') {
return parsed as User;
}
return null;
} catch {
return null;
}
}
const user = parseUser('{"name": "Иван", "age": 25, "city": "СПб"}');
if (user) {
console.log(`Пользователь: ${user.name}, возраст: ${user.age}`);
}
Такой подход предотвращает ошибки времени выполнения, если сервер вернёт некорректные данные.
Сериализация: JSON.stringify()
Метод JSON.stringify() преобразует объект JavaScript в строку JSON. В TypeScript этот метод работает так же, как в JavaScript, но с учётом типов.
Простая сериализация
const user: User = {
name: 'Мария',
age: 28,
city: 'Казань'
};
const jsonString = JSON.stringify(user);
console.log(jsonString); // {"name":"Мария","age":28,"city":"Казань"}
Параметры форматирования
Для читаемого вывода используйте параметры replacer и space:
const user = {
name: 'Алексей',
age: 35,
city: 'Новосибирск',
password: 'secret123'
};
// Исключаем поле password
const jsonString = JSON.stringify(user, ['name', 'age', 'city'], 2);
console.log(jsonString);
/* Вывод:
{
"name": "Алексей",
"age": 35,
"city": "Новосибирск"
}
*/
Параметр replacer может быть функцией для более гибкой фильтрации:
const jsonString = JSON.stringify(user, (key, value) => {
if (key === 'password') return undefined; // исключаем
return value;
}, 2);
Сериализация вложенных объектов и массивов
interface Company {
name: string;
employees: User[];
}
const company: Company = {
name: 'TechCorp',
employees: [
{ name: 'Ольга', age: 32, city: 'Екатеринбург' },
{ name: 'Дмитрий', age: 29, city: 'Ростов' }
]
};
const jsonString = JSON.stringify(company, null, 2);
console.log(jsonString);
/* Вывод:
{
"name": "TechCorp",
"employees": [
{
"name": "Ольга",
"age": 32,
"city": "Екатеринбург"
},
{
"name": "Дмитрий",
"age": 29,
"city": "Ростов"
}
]
}
*/
Продвинутые техники работы с JSON
Дженерики для универсального парсинга
Используйте дженерики для создания универсальной функции парсинга:
function safeParseJSON<T>(json: string, validator: (data: unknown) => data is T): T | null {
try {
const parsed = JSON.parse(json);
if (validator(parsed)) {
return parsed;
}
return null;
} catch {
return null;
}
}
// Функция-валидатор для типа User
function isUser(data: unknown): data is User {
return (
typeof data === 'object' && data !== null &&
typeof (data as User).name === 'string' &&
typeof (data as User).age === 'number' &&
typeof (data as User).city === 'string'
);
}
const user = safeParseJSON<User>(
'{"name": "Елена", "age": 27, "city": "Самара"}',
isUser
);
if (user) {
console.log(`Привет, ${user.name}!`);
}
Работа с датами и специальными типами
JSON не поддерживает тип Date напрямую. При сериализации даты преобразуются в строки:
interface Event {
title: string;
date: Date;
participants: number;
}
const event: Event = {
title: 'Конференция',
date: new Date('2025-06-15'),
participants: 200
};
const json