Структурная и номинальная типизация
🟣 Номинальная типизация (nominal typing) - это когда совместимость типов определяется по их имени или идентификаторам, а не по внутренней структуре.
Даже если структура одинаковая - типы разные.
Два типа считаются совместимыми, только если они явно связаны иерархией.
Это более строгий и безопасный, но менее гибкий подход по сравнению со структурной типизацией, характерный для языков типа Java и C#.
Рассмотрим пример кода на Java (скрин 1).
У нас есть класс User и класс Admin. И если мы попытаемся создать переменную user типа User и присвоить в нее инстанс класса Admin, то будет ошибка, потому что в джаве номинальная типизация и фактически эти типы разные.
🟣Структурная типизация (structural typing) - это система проверки типов, где совместимость определяется не по имени, а по внутренней структуре.
TypeScript как раз использует структурную типизацию, то есть:
Совместимость типов определяется по структуре - если объект имеет все требуемые свойства и методы, он считается совместимым, независимо от имени типа.
Тот же самый пример (скрин 2).
TypeScript разрешает это присваивание, потому что у обоих типов одна и та же структура (свойство name типа string).
🟣Но мы можем заставить типы в классах вести себя как номинальные, для это нужно добавить приватное поле (скрин 3).
Когда мы объявляем поле с ключевым словом private, то TS применяет ограничение на уровне компиляции. Это означает, что приватное поле может быть доступно только внутри того же класса.
Поэтому два класса, которые имеют приватные поля с одинаковыми именами - структурно несовместимы, так как тайпскрипт будет учитывать "происхождение" приватных полей.
При необходимости номинальное поведение так же можно симулировать через брендирование (расскажу в отдельном посте).
🟣Структурная типизация хоть и считается "гибкой", но может привести к неожиданным проблемам. Рассмотрим следующий пример (скрин 4):
У нас есть тип DatabaseConfig, который имеет обязательное поле url и необязательное поля retries. И тип APIConfig, который также имеет обязательное поле url и необязательно поле timeout.
Есть функция startApi, которая принимает конфиг, который должен быть типа APIConfig.
Но из-за структурной типизации мы можем передать внутрь функции переменную, которая имеет другой тип, так как их структура типов совпала.
ТС это разрешил, но в реальности внутри функции мы не сможем обращаться к полю retries - будет ошибка.
В реальности подобные проблемы можно ловить через сотни строчек кода и десятки файлов. Поэтому я считаю, что наличие структурной типизации в ТС - это большое допущение, которое приходится фиксить дополнительными танцами с бубном ради безопасной системы типов.
✈️ Telegram | 🎓 Менторство | 📹 YouTube | 👩💻 Roadmap
Post #106
2.78K




- 🔥 25
- 👍 8
- ✍ 7
- ❤ 3