Cегодня хочется поговорить о брендированных типах в typescript. Для начала, представим, что у нас есть функция для конвертации валют.

const usdToEur = (usdAmount: number, rate = 0.92) => usdAmount * rate;
usdToEur(100); // === 92 eur

Давайте немного улучшим ее, введя типы USD и EUR, чтобы не только название функции и коментарии к ней подсказывали нам о ее предназначении.

type USD = number;
type EUR = number;

const usdToEur = (usdAmount: USD, rate = 0.92): EUR => usdAmount * rate;
usdToEur(100); // === 92 eur

Стало гораздо лучше, но фактически ничего не изменилось, тк мы можем также передавать любое число и нововведенные типы не защищают нас от ошибки. И здесь нам на помощь приходят брендированные типы. Смысл в том, что мы подмешиваем в описание типа дополнительные поля, которые по факту (в рантайме) не существуют, но при этом typescript осуществляет по ним проверку.

type USD = number & { __type__: 'USD' };
type EUR = number & { __type__: 'EUR' };

const usdToEur = (usdAmount: USD, rate = 0.92): EUR => (usdAmount * rate) as EUR;

// usdToEur(100); // type error
usdToEur(100 as USD);

И чтобы легче все это было использовать есть микробиблиотека https://github.com/kourge/ts-brand

import {Brand, make} from 'ts-brand';
type USD = Brand<number, 'USD'>;
type EUR = Brand<number, 'EUR'>;

const makeEur = make<EUR>();
const usdToEur = (usdAmount: USD, rate = 0.92): EUR => makeEur(usdAmount * rate);