Как можно типизировать работу с JSON-объектами в TypeScript?

Работа с JSON в TypeScript — распространённая задача, поскольку JSON является стандартным форматом обмена данными между клиентом и сервером. Однако при парсинге JSON мы получаем объект типа any, и это лишает нас преимуществ статической типизации. Чтобы избежать ошибок и сохранить безопасность кода, важно правильно типизировать такие объекты.

Проблема при работе с JSON

Функция JSON.parse() всегда возвращает значение типа any, потому что на этапе выполнения TypeScript не может знать структуру входных данных. Это значит, что без явной типизации мы теряем проверку типов и можем, например, обратиться к несуществующему свойству:

const data = JSON.parse('{"name":"Alice"}');
console.log(data.age.toFixed()); // ошибка только в рантайме

Использование интерфейсов или типов

Чтобы типизировать JSON-объект, обычно создают интерфейс или тип, описывающий ожидаемую структуру:

interface User {
name: string;
age: number;
}
const json = '{"name":"Alice","age":25}';
const user: User = JSON.parse(json);

Здесь мы явно указываем, что результат парсинга должен соответствовать интерфейсу User. Но нужно понимать, что компилятор просто доверяет этому утверждению — реальной валидации структуры не происходит.

Приведение типов с помощью as

Иногда применяют приведение типов:

const user = JSON.parse(json) as User;

Это упрощает запись, но также не гарантирует корректность данных. Если в JSON не будет нужных полей или типы окажутся другими, ошибка проявится только в рантайме.

Использование дженериков для типизации

Часто создают обёртку над JSON.parse(), чтобы удобнее работать с типами:

function parseJSON<T>(str: string): T {
return JSON.parse(str) as T;
}
const user = parseJSON<User>('{"name":"Alice","age":25}');

Теперь при вызове можно передавать дженерик, и компилятор будет проверять дальнейшую работу с объектом по указанному типу.

Валидация данных после парсинга

Так как TypeScript проверяет типы только на этапе компиляции, для защиты от некорректных данных в JSON часто используют дополнительные проверки. Это можно сделать вручную:

function isUser(obj: any): obj is User {
return typeof obj.name === "string" && typeof obj.age === "number";
}
const parsed = JSON.parse(json);
if (isUser(parsed)) {
console.log(parsed.name);
}

Здесь мы описали type guard, который гарантирует, что объект соответствует интерфейсу User.

Работа с внешними библиотеками

В реальных проектах часто применяют библиотеки для валидации и типизации JSON, например zod или io-ts. Они позволяют не только задать типы, но и валидировать входящие данные во время выполнения. Это особенно важно при работе с API, где формат ответа может отличаться от ожидаемого.

Типизация при сериализации в JSON

При использовании JSON.stringify() проблем с типами обычно меньше, так как на вход подаётся уже типизированный объект:

const user: User = { name: "Alice", age: 25 };
const jsonString = JSON.stringify(user);

В этом случае статическая типизация гарантирует, что сериализуемый объект соответствует описанному интерфейсу.