Как стать автором
Обновить

Комментарии 13

>В данной статья я расскажу о том, как решить эту проблему средствами TSLint
Причем тут tslint?

Если TSLint подключен к редактору, он в на этапе написания программы подсветит неверную подстановку переменной сообщением об ошибке вида


Type 'BookId' is not assignable to type 'AuthorId'.
Это сделает typescript, а не tslint
Спасибо за замечание, сейчас исправлю статью.

Вот только при таком подходе у идентификаторов появляется какое-то левое поле _type. Лучше использовать символ:


const flavorType = Symbol("flavorType");

export type Flavor<T, FlavorT> = T & { [flavorType]?: FlavorT };

По-хорошему, ещё и строки "CarId" с "BoatId" нужно за символами спрятать, но TS пока что так не умеет.

Да, вместо _type можно использовать как _myProjectNameType, так и Symbol, если есть опасения из-за возможного конфликта имен. Главное, что после компиляции из TypeScript в JavaScript эта информация о типах будет удалена.
Кстати, сами разрабочики TypeScript в своих исходниках используют брендинг. Поэтому использование левого поля _type можно считать допустимым.

Интересное решение, но непонятно, насколько оно стабильное. Может оказаться, что в будущих версиях Typescript станет умнее, и будет игнорировать поле `_type` потому что оно опциональное и нигде не используется. Тогда CarId и BoatId внезапно станут одинаковым типом.

Хотелось бы узнать, кто что думает об этом аспекте.
Typescript не может знать что свойство нигде не используется потому что всегда может существовать какой-то неизвестный TS модуль к этому самому свойству обращающийся.
>> Эмулируем самую строгую типизацию
Здесь опечатка: type BoatId = number & { _type: 'BookId'};

Все мы поняли что там должно быть BoatId. Но это тот самый случай, когда могут 2 разных типа проскочить по одному имени :(

Кстати, как-то копался на github и newtype-ts
Спасибо за уточнение. Сейчас исправлю статью. Изначально хотел сделать примеры с книгами и авторами — при переименовании проскочило старое название :)

Можно ещё так:


declare const CarSymbol: unique symbol;
type CarId = string & typeof CarSymbol;

declare const BoatSymbol: unique symbol;
type BoatId = string & typeof BoatSymbol;

function processCar(id: CarId) {}
function processBoat(id: BoatId) {}
function processString(value: string) {}

let carId = <CarId>'car uuid';
let boatId = <BoatId>'boat uuid';

processCar(carId);
processCar(boatId); // error

processBoat(carId); // error
processBoat(boatId);

processString(carId); // ok
processString(boatId); // ok

Вообще горячий топик
https://github.com/Microsoft/TypeScript/issues/202

Выглядит как багоюз. Тип string & symbol должен быть эквивалентен never, и однажды в очередной версии компилятора он им и станет...

Самая удобная реализация, что я смог придумать, есть в $mol_data_nominal.


const Weight = $mol_data_nominal({ Weight : $mol_data_integer })
const Length = $mol_data_nominal({ Length : $mol_data_integer })

let len = Length(10)
len = Length(20) // Validate
len = 20 as typeof Length.Value // Cast

len = 20 // Compile time error
len = Weight(20) // Compile time error
len = Length( 20.1 ) // Run time error
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации