Содержание
TypeScript - очень популярный инструмент JavaScript, имеющий на момент написания статьи более 94,7 тыс. звезд на GitHub.
Конечно, есть и 5 676 открытых проблем, из которых только 1 693 обозначены как ошибки.
Я вкратце упомяну о проблемах с этим, но это все равно очень полезный инструмент.
Важным фактом является то, что удобство использования TypeScript сильно зависит от того, насколько хорошо написаны типы.
В этой статье я расскажу о некоторых советах по TypeScript и JSDoc, которые я использовал при написании простых библиотек.
Более интеллектуальный тип union
Давайте рассмотрим случай простого определения типов Bird и Fish.
Тип Animal имеет функцию eat, а Bird и Fish имеют функции fly и swim.
тип Животное = {
eat(food: any): void;
};
type Bird = {
type: 'Bird';
fly(): void;
} & Animal;
type Fish = {
type: 'Рыба';
swim(): void;
} & Animal;
Приведенные выше типы кажутся подходящими, но они не очень удобны.
Давайте, например, напишем функцию move.
function move(animal: Animal) {
if (animal.type == 'Bird') {
(animal as Bird).fly();
} else if (animal.type == 'Fish') {
(animal as Fish).swim();
}
}
Логически кажется, что можно использовать значения type для использования защит типа, но на самом деле это не так.
Ниже описано, как исправить приведенные выше типы, чтобы они работали логически.
тип BaseAnimal = {
eat(food: any): void;
};
type Bird = {
тип: 'Bird';
fly(): void;
} & BaseAnimal;
type Fish = {
type: 'Рыба';
swim(): void;
} & BaseAnimal;
type Animal = Bird | Fish;
Чтобы использовать защиту типов, Animal необходимо сделать объединенным типом. Реальный пример: dom-eater
Поддержка более сильной проверки типов с помощью infer.
Ключевое слово infer - это функция, которая может быть использована для вывода типов в коде с помощью ключевого слова extends.
Используя infer для реализации таких типов, как Parameters и ReturnType, или напрямую используя infer, вы можете обеспечить еще более сложную и мощную поддержку типов.
Например, чтобы усилить проверку типа setTimeout с помощью Parameters, вы можете написать код следующим образом.
declare function setTimeout<T extends (...args: any[]) => any>(
обработчик: T,
таймаут?: число,
...arguments: Parameters<T>
): число;
// Аргумент типа 'number' не может быть присвоен параметру типа 'string'.ts(2345)
setTimeout((a: string, b: number) => a + b, void 0, 123, 123);
// всё в порядке
setTimeout((a: string, b: number) => a + b, void 0, 'abc', 123);
На самом деле, setTimeout может выполнить eval, передав строку в качестве обратного вызова, но мы не будем рассматривать эту часть.
Реальный пример: async-lube
Избегайте d.ts и оптимизируйте ts.map, максимизируя вывод типов.
Определение файлов d.ts для сложных типов не только очень утомительная задача, но и заставляет IDE переходить к файлам d.ts вместо реальных исходников при использовании функции отслеживания исходников с помощью файлов ts.map.
Если вы используете ключевое слово infer и ключевое слово typeof, представленные выше, чтобы максимально использовать вывод типов из реального кода, вы можете определять типы более просто, поддерживать сильную проверку типов, а также пользоваться преимуществами файлов ts.map.
На этом я остановлюсь только на реальных примерах использования и пойду дальше.
Реальный пример: async-lube
Не удивляйтесь ошибкам в TypeScript
Как уже говорилось, в TypeScript много ошибок.
Одна из критических ошибок, с которой я столкнулся, заключается в том, что при использовании ключевого слова typeof в родовой функции при генерации файлов d.ts с помощью tsc и JSDoc компилятор не отражает его должным образом и преобразует к любому типу.
Однако в TypeScript много ошибок, и участие в проекте довольно обременительно.
Даже чтобы сообщить об ошибке, нужно просмотреть тысячи списков проблем, чтобы понять, не является ли она уже известной.
Если вы столкнулись с ошибкой, лучший способ - создать PR для ее исправления, но при этом следует учитывать и неудобные решения.
Я решил описанную выше ошибку, написав скрипт для преобразования типа возврата любого типа JSDoc.
автогенерируемого d.ts
Я приложу игровую площадку для некоторых TypeScriжуки.
TypeScript Bugs
JSDoc d.ts Bugs
Простые советы по использованию преобразований типов в JSDoc
Синтаксис JSDoc, безусловно, не так красив, как TypeScript, но самым уникальным является синтаксис приведения типов.
TypeScript перенял этот синтаксис у Google Closure, но я думаю, что было бы лучше создать синтаксис типа @cast.
Простое решение, которое я придумал для этой проблемы, заключается в добавлении дополнительных комментариев после синтаксиса приведения типов.
// ts
const value = func(something as SomeType);
// JSDoc
const value = func(/\*_ @type{SomeType} _/ something);
// JSDoc с дополнительным комментарием
const value = func(/** @type{SomeType} \*/ something /**/);
Это может показаться еще более громоздким, но я лично рекомендую этот метод, потому что читабельность кода значительно улучшилась, когда я использовал его.
Заключение
За последние 10 лет TypeScript стал очень удобным инструментом. Если вы будете обращаться с ним правильно, как описано в этой статье, TypeScript доставит вам огромное удовольствие при написании кода. Однако множество проблем, существующих в проекте TypeScript, ослабило мое желание внести свой вклад и затруднило реальное участие в проекте. Я прокомментировал эту проблему, но поскольку о ней было сообщено несколько лет назад, я не ожидаю, что она будет решена в ближайшее время. Тем не менее, TypeScript уже является очень привлекательным инструментом, поэтому я надеюсь, что вы будете наслаждаться удобством, которое этот инструмент предоставляет. Спасибо.