TypeScript unknown вместо any: проверка JSON на границе приложения

TypeScript unknown вместо any: проверка JSON на границе приложения
Содержание статьи

Данные внешнего ответа нельзя сделать достоверными одной аннотацией TypeScript. Тип unknown заставляет проверить значение перед использованием, тогда как any позволяет обходить значительную часть статической проверки. На Mentoring.digital разберём небольшую границу JSON-входа без принудительного утверждения типа через as.

Сначала задайте контракт

Пусть приложение ожидает объект с полем name, содержащим строку. Это учебный контракт, а не готовая схема любого профиля. Сначала определите обязательность поля и допустимость пустой строки. Если требования неизвестны, компилятор не сможет выбрать их вместо команды.

type Profile = { name: string };

function parseProfile(value: unknown): Profile {
  if (typeof value !== "object" || value === null) {
    throw new Error("Expected an object");
  }
  if (!("name" in value) || typeof value.name !== "string") {
    throw new Error("Expected a string name");
  }
  return { name: value.name };
}

Проверка null нужна отдельно, поскольку typeof null возвращает object. Далее проверяется существование свойства и его значение. Функция возвращает новый ограниченный объект, не распространяя все неизвестные поля входа по приложению. Это сознательное решение примера, а не обязательный подход для любой схемы.

Утверждение типа не проверяет JSON

Выражение as Profile сообщает компилятору выбранное разработчиком предположение. Оно не добавляет проверку ответа во время выполнения. Если поле отсутствует или имеет другой тип, написанная аннотация сама по себе не исправит данные.

const raw: unknown = JSON.parse('{"name":"Аня"}');
const profile = parseProfile(raw);
console.log(profile.name);

Разбор JSON и проверка структуры являются разными этапами. Некорректный JSON может завершиться ошибкой до вызова parseProfile, а синтаксически правильный JSON может не соответствовать ожидаемому объекту. При обработке ошибок полезно различать эти причины и показывать подходящее состояние интерфейса.

Не теряйте допустимые значения

Пример допускает пустую строку, потому что проверяет именно тип. Если нужен непустой текст, добавьте отдельное правило с понятной нормализацией. Не заменяйте проверку случайным условием if(value.name): оно смешивает существование, тип и истинность значения.

Если контракту нужны массивы, вложенные объекты или несколько вариантов ответа, проверяйте каждый значимый уровень. Один typeof у корня не подтверждает внутреннюю структуру. Для большой схемы можно рассмотреть библиотеку проверки, но её выбор и правила нужно согласовать с проектом, а не добавлять только ради короткого примера.

Какие случаи проверить

Подготовьте корректный объект, отсутствие name, числовое name, null и строку вместо объекта. Для текущего контракта пустая строка должна приниматься. Отдельно проверьте синтаксически повреждённый JSON на этапе JSON.parse. Эти случаи проверяют разные границы, поэтому один успешный пример недостаточен.

TypeScript проверяет статическую совместимость, а ваши условия работают при выполнении. Не называйте пример протестированным только потому, что редактор не подчёркивает строки. Для своего проекта выполните проверку компилятора с нужными настройками и проверки поведения, сохранив версии и результаты отдельно.

Где использовать такую границу

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

Если непонятно, где проходит эта граница в приложении, подготовьте схему потока и пример для ментора по TypeScript. Цель разбора: явный контракт и наблюдаемая обработка недопустимых данных, а не удаление всех предупреждений компилятора любой ценой.

Похожие статьи по данной теме