Токен

Токен используется для идентификации зависимостей и мог бы быть уникальной строкой или числом. Но кроме идентификации нужен контроль типов, чтобы в регистрации зависимостей можно было указать идентификатор только на сопоставимый тип программного решения, чтобы гарантировать корректность самой регистрации. Поэтому к уникальному значению привязывается тип. Связка идентификатора с полезной нагрузкой (типом) называется токеном. Для создания токена используется утилита:

function newToken<Type>(name: string): Token<Type>

При создании токена необходимо обязательно указать связываемый тип программного объекта и уникальную строку в качестве названия токена. Для обеспечения уникальности названия предлагается использовать формат URI, например "my-project/my-service". Новые токены желательно создавать в отдельных файлах, чтобы можно было импортировать их отдельно от реализации программного решения.

// Токен на тип MyService (класс или интерфейс сервиса)
export const MY = newToken<MyService>('my-project/my-service')
// Токен на тип MyServiceConfig (настройки сервиса)
export const MY_CONFIG = newToken<MyServiceConfig>('my-project/my-service/config')

Константа MY будет объектом с типом Token<MyService>. Токен — это не строка, а объект, что позволяет в токене хранить дополнительную полезную информацию, в частности атрибут опциональности.

Опциональный токен optionalToken()

Опциональный токен создаётся через декорирование обычного токена утилитой optionalToken():

function optionalToken<Type>(token: Token<Type>): TokenDecorator<Type | undefined>

Утилита optionalToken принимает оригинальный токен и возвращает новый токен, внутри которого будет ссылка на оригинальный токен. Новый токен реализует паттерн проектирования «Декоратор». Уникальное значение нового токена будет браться из оригинального токена, но новый токен будет подставлять свой предопределённый атрибут опциональности. Оригинальный токен не меняется и не знает, что на него ссылается другой токен. Заранее создавать опциональный токен не нужно, достаточно декорировать оригинальный токен на месте применения.

const MY_OPTIONAL = optionalToken(MY) // :TokenDecorator<MyService | undefined>

console.log(MY_OPTIONAL.is('optional')) // true
console.log(MY_OPTIONAL.key === MY.key) // true

Опциональный токен MY_OPTIONAL связан с типом MyService | undefined и может применяться для указания опциональных зависимостей. Если зависимость опциональная, то подойдет и оригинальный токен, но если в DI контейнере не будет соответствующей регистрации, то контейнер выкинет исключение, так как оригинальный токен не допускает отсутствия значения. С опциональным токеном ошибок не будет и в зависимость подставится undefined. За счёт строгой типизации опциональный токен не получится ошибочно указать для обязательной зависимости.

Итог

Токены создаются для новых решений с целью идентификации их программного интерфейса (типа), чтобы эти решения можно было внедрить в DI контейнер и использовать как зависимости в других решениях. Токены также создаются для составных частей новых решений, если предполагается получать их как зависимости. Например, токен создаётся для настроек сервиса.

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

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

Токены связаны с типом решения, что позволяет автоматчики выводить типы при применении токена. Например, контейнер заранее не знает, какие регистрации будут в него добавлены, и у контейнера нет агрегированного типа для всех регистраций. Только благодаря токену метод get будет выводить тип возвращаемого решения. Токен обеспечивает слабую связанность в системе типов.

Внедрение зависимостей | Регистрация