Ответ вслух Middle Объединения и сужение обновлено

Типизация данных из API

Вопрос

Как безопасно типизировать ответ сервера? Чем плохо const user = await res.json() as User?

Попробуйте сами

Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.

Показать ответ и разбор

Коротко

as User это обещание компилятору, а не проверка. Типы стираются, и если бэкенд переименует поле или вернёт null, приложение упадёт где-то в рендере, далеко от запроса.

Что должно прозвучать в ответе

  • Типы стираются, `as` ничего не проверяет в рантайме

  • Если сервер вернёт другую форму, ошибка проявится далеко от места запроса

  • Правильно принимать данные как `unknown` и сужать

  • Для сложных форм использовать схему (zod, valibot): один источник для проверки и для типа через `z.infer`

  • Ещё вариант: генерация типов и клиентов из OpenAPI, но рантайм-проверку на границе это не заменяет

Что могут спросить следом

  • Где проводить валидацию: в каждом компоненте или в одном слое?
  • Что делать при невалидном ответе?
  • Чем type guard хуже схемы?

Разбор

as User это обещание компилятору, а не проверка. Типы стираются, и если бэкенд переименует поле или вернёт null, приложение упадёт где-то в рендере, далеко от запроса. Поэтому всё, что приходит извне, я считаю unknown и сужаю на границе: в слое API-клиента. Для маленьких форм хватит type guard, но он ручной и легко врёт. Для реальных DTO беру схему, например zod: User.parse(json) проверяет данные в рантайме, а z.infer<typeof User> даёт тип, и они не разъезжаются. Если есть OpenAPI, генерирую из него типы, но это синхронизирует контракт на этапе сборки, а не проверяет конкретный ответ. При невалидном ответе бросаю понятную ошибку с логированием, чтобы UI показал состояние ошибки, а не сломался на undefined.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.