Ответ вслух Senior Классы и интерфейсы обновлено

Номинальные типы в структурном TypeScript

Вопрос

TypeScript структурный. Как сделать так, чтобы UserId и OrderId (оба строки) нельзя было перепутать? Какие есть способы и их цена?

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

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

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

Коротко

В структурной системе две строки неразличимы, поэтому deleteUser(order.id) компилируется. Самый лёгкий способ сделать номинальность: branded type, пересечение базового типа с фантомным полем, которого нет в рантайме.

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

  • Проблема: структурная типизация считает одинаковые формы совместимыми

  • Branded type: `string & { readonly [brand]: "UserId" }`, создаётся только через функцию-парсер

  • Класс с `private`/`#`-полем: совместимость только с тем же объявлением

  • Цена: приведение `as` в одном месте, бренд теряется при арифметике и сериализации

  • Библиотечные варианты: `z.string().brand()`, `Brand` в Effect

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

  • Почему unique symbol лучше строкового ключа __brand?
  • Что будет с брендом после JSON.stringify и обратно?
  • Стоит ли брендировать всё подряд?

Разбор

В структурной системе две строки неразличимы, поэтому deleteUser(order.id) компилируется. Самый лёгкий способ сделать номинальность: branded type, пересечение базового типа с фантомным полем, которого нет в рантайме. Лучше ключом сделать declare const brand: unique symbol, тогда объект с таким полем руками не собрать. Значение получает бренд только в функции-парсере или конструкторе с проверкой, это единственное место с as. Альтернатива для объектов: класс с приватным полем, такие экземпляры несовместимы с похожими по форме. Цена: бренд теряется при арифметике над числами и при сериализации, после JSON.parse значение надо снова провести через парсер, а избыточное брендирование раздражает команду. Поэтому брендирую идентификаторы, деньги и проверенные строки (email), а не всё подряд.

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

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