ЯдроКодаподготовка к экзаменам
Учебная платформа

Загружаем материалы

Подготавливаем материалы и навигацию по разделу.

Satisfies в TypeScript: проверка конфигурации с сохранением вывода типов

Автор: · Обновлено

satisfies проверяет совместимость выражения с выбранным типом, не заменяя его обычной широкой аннотацией. Это особенно удобно для конфигураций: хочется проверить обязательные…

satisfies проверяет совместимость выражения с выбранным типом, не заменяя его обычной широкой аннотацией. Это особенно удобно для конфигураций: хочется проверить обязательные ключи и допустимые значения, сохранив полезную информацию о каждом конкретном свойстве.

Цель и необходимые знания

Понадобятся литеральные типы, Record и понимание различия между аннотацией и утверждением. В этом уроке мы сравним три способа описания конфигурации, рассмотрим влияние контекста на вывод и отделим статическую проверку от валидации пользовательского ввода. Основной пример работает в TypeScript 6 с strict; сам оператор доступен начиная с TypeScript 4.9.

Проверенный договор без лишнего расширения

Пусть настройки виджета содержат подпись заголовка и ширину боковой панели. Для учебного примера значения допускаются двух видов: строка либо число. Если объявить объект как Record<Slot, string | number>, при чтении каждого свойства компилятор должен учитывать оба варианта. Но в нашем литерале заголовок явно строковый, и потеря этой информации мешает сразу использовать строковый метод.

export {};

type Slot = 'heading' | 'sidebar';
type LayoutSettings = Record<Slot, string | number>;

const settings = {
  heading: 'Новые занятия',
  sidebar: 280,
} satisfies LayoutSettings;

console.log(settings.heading.toUpperCase());
console.log(settings.sidebar + 20);

const annotated: LayoutSettings = { heading: 'Архив', sidebar: 240 };
if (false) {
  // @ts-expect-error Широкая аннотация допускает number у heading.
  annotated.heading.toUpperCase();
  // @ts-expect-error В договоре обязательна настройка sidebar.
  const incomplete = { heading: 'Архив' } satisfies LayoutSettings;
  void incomplete;
}

Вывод — НОВЫЕ ЗАНЯТИЯ и 300. У объекта settings проверены оба обязательных ключа, но конкретные свойства сохранили пригодные типы. Если написать лишний ключ в свежем литерале, проверка также поможет заметить опечатку. Этот эффект полезен для словаря подписей, таблицы обработчиков и реестра настроек, которые разрабатываются вместе с кодом.

Аннотация, as и satisfies отвечают на разные вопросы

Аннотация говорит: дальше рассматриваем переменную через указанный договор. Это правильно для изменяемой переменной, которая должна принимать разные подходящие объекты. as просит компилятор принять утверждение разработчика, но не добавляет проверяющий код. satisfies проверяет совместимость при описании выражения и оставляет результат пригодным для более точной дальнейшей работы.

Из этого не следует, что satisfies всегда предпочтительнее. Возвращаемый тип публичной функции часто полезно объявлять явно, чтобы случайная деталь реализации не стала частью её интерфейса. Конфигурацию с намеренно фиксированными ключами удобно проверять через satisfies. Место применения выбирают по ответственности за последующие изменения, а не по моде на конкретное ключевое слово.

As const и контекст вывода

Комбинация as const satisfies ... одновременно сохраняет константную форму литерала и проверяет её совместимость с договором. При работе с массивами проверьте требования к изменяемости: readonly-кортеж нельзя бездумно передавать коду, который вправе изменять массив. Иногда корректный договор — readonly Item[], иногда нужен обычный изменяемый массив с явной аннотацией. Требования потребителя определяют решение.

Важная тонкость: выражение получает контекст для вывода типов. Поэтому результат с satisfies не обязан полностью совпадать с типом такого же литерала вне контекста. Например, поле с исходным true может сохранить литеральный тип true, и последующее присваивание false окажется ошибкой. Если поле задумано как переключатель, задайте подходящую изменяемую модель, вместо неожиданного применения утверждения при каждом изменении.

export {};

const initial = { enabled: true } satisfies { enabled: boolean };
const editable: { enabled: boolean } = { enabled: true };
editable.enabled = false;
console.log(editable.enabled);

if (false) {
  // @ts-expect-error Контекст сохранил у initial.enabled литеральный true.
  initial.enabled = false;
}

Этот пример выводит false. Он нужен, чтобы не запоминать слишком сильный лозунг «satisfies никогда не влияет на тип». Полезнее задавать конкретный вопрос: какой тип получило интересующее свойство и какие последующие присваивания должны быть разрешены? Ответ можно проверить короткой компилируемой пробой.

Где заканчивается проверка конфигурации

satisfies не читает HTTP-ответ и не проверяет JSON во время выполнения. Если переменная уже имеет тип any, оператор не восстановит потерянное доверие к данным. Если значение имеет unknown, простое желание объявить его подходящим не заменяет проверку формы. Внешняя конфигурация проходит runtime validation, а затем используется внутри приложения с известным типом.

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

Типичные ошибки

Не заменяйте as const на satisfies, если задача состояла именно в сохранении константных литералов. Не используйте широкую индексную сигнатуру Record<string, ...>, ожидая проверки конкретного обязательного набора имён: для этого нужен конечный тип ключей. Не пытайтесь компенсировать неверный договор многократными утверждениями. Сначала решите, что действительно разрешено менять, и проверьте тип одного свойства.

Практикум: словарь сообщений редактора

Создайте тип состояний 'saved' | 'saving' | 'failed' и словарь, где каждому состоянию соответствует либо строка, либо функция с числовым аргументом и строковым результатом. Для saved и failed задайте строки, для saving — функцию, показывающую процент. Проверьте полноту словаря через satisfies, затем вызовите метод строки у saved и функцию у saving без дополнительных утверждений.

Разбор: конечное множество ключей позволяет заметить пропущенную ветвь, а контекст значения задаёт допустимые варианты элементов. Конкретные свойства сохраняют строковую или функциональную форму. Ожидаемая проверка включает удаление failed, опечатку в имени и неправильный результат функции. Для изменяемого пользовательского словаря потребуется другой договор: он может намеренно позволять замену строки функцией, чего не следует автоматически требовать от статической конфигурации.

Частые вопросы

Satisfies заменяет интерфейс или type?

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

Почему данные из JSON не становятся безопасными после satisfies?

Проверяется информация, уже известная компилятору, а не содержимое документа при запуске программы. Недостоверная аннотация или any не превращаются в проверенный формат. Для внешнего документа нужны разбор и реальные проверки, после которых можно работать с результатом известного типа.

Источники