Сперва позвольте уточнить: типизированный JavaScript — это фантастика. Я использовал Flow и продолжу использовать TypeScript в будущем, ведь это мощнейший быстро растущий инструмент.
Но какова цена этой силы? Что стоит за этим инструментом, буквально меняющим правила игры?
Иногда стоит пристальнее рассмотреть все хорошие и плохие стороны своего инструмента.
Поэтому, пожалуйста, отложите свои 8-битные факелы и вилы ненадолго и давайте без предубеждений изучим негативные аспекты типизированного JavaScript!
Код легко становится многословным
Я рассматриваю эту тему в основном с точки зрения React + Redux. Ваш опыт будет во многом зависеть от используемых библиотек!
Ирония использования TypeScript и Flow в том, что больше всего нам хочется избежать ручной типизации! И это может сделать код менее управляемым, многословным и подвергнутым ошибкам.
В идеальном мире мы позволяем TypeScript выводить типы из уже определённых в языке и добавленных библиотек. Таким образом мы получаем преимущество безопасной типизации, управляя только пользовательскими типами объектов.
Однако иногда нельзя избежать многословности. Давайте посмотрим на простой компонент React на основе классов, написанный на TypeScript:
interface NameProviderProps {
children: (state: NameProviderState) => React.ReactNode;
}
interface NameProviderState {
readonly name: string;
}
export class NameProvider extends React.Component<NameProviderProps, NameProviderState> {
readonly state: NameProviderState = { name: 'Piotr' };
render() {
return this.props.children(this.state);
}
}
Вот сам компонент React:
export class NameProvider extends React.Component {
state = { name: 'Piotr' };
render() {
return this.props.children(this.state);
}
}
Версия TypeScript содержит на 248% больше кода. Да, props и состояния понятно определены, но неужели мы действительно называем это читабельностью?
Просто посмотрите на этот пример типа для обработки повторных действий. Он очень понятный и полезный — я очень рекомендую прочесть его . Но зачем он нужен?
type InferValueTypes<T> = T extends { [key: string]: infer U } ? U : never;
type Actions = ReturnType<InferValueTypes<typeof actions>>
Это действительно уродливая типизация…
Многие библиотеки не содержат типы
Пока типизированная экосистема растёт, она не покрывает 100% кода. Это означает, что во многих случая вам нужно развёртывать типы вручную.
В какой-то момент каждый крупный проект приходит к развёртыванию нескольких странных пользовательских типов (или копированию их с GitHub), чтобы приспособить очередную библиотеку или вариант использования.
Даже самые популярные библиотеки, такие как Redux, могут превратить управление действиями, преобразователями и прочие в настоящий ад.
Написанные вручную типы подвержены ошибкам
Давайте представим большую команду, работающую над проектом. Вася написал следующее определение типа:
type PropsA = {
name: string,
date: Date,
url: string
}
В то же самое время Коля, который не знал о существовании этого типа, создал подобный:
type PropsB = {
name: string,
date: string,
url: string
}
Теперь у нас есть повторяющиеся типы, и, что ещё хуже, PropsB
определён немного иначе. И все обещания понятного определения типов идут насмарку.
И это не гипотетический сценарий, который редко встречается — я видел это в крупных многомиллионных проектах.
Ложное ощущение безопасности
Скажем, вы определили повторяющееся действие как:
export const MY_BASIC_ACTION: ‘MY_BASIC_ACTION’ = ‘MY_BASIC_ACTION’
Работать с несколькими подобными типами вполне можно, но когда у вас 30 типов действий, следующая ситуация может легко возникнуть при копировании старого действия в качестве шаблона для нового:
export const MY_NEW_ACTION: ‘MY_BASIC_ACTION’ = ‘MY_BASIC_ACTION’
Видите? Мы только что типизировали новое действие так же, как первое.
И это крайне трудно отследить, так как вы отправляете MY_NEW_ACTION
, но в действительности отправится MY_BASIC_ACTION
.
Вы почувствуете себя настолько безопасно и приятно в объятиях Flow или TypeScript, что будете биться головой об стену часами, прежде чем поймёте, что это… ошибка типов!
Управление типами требует мастерства
JavaScript уже чудовищен для понимания. Когда вы добавляете типы к настолько динамичному, беззаботному языку, требуются серьёзные навыки для корректного и продуктивного написания типов.
Это не повод избегать типизированного JS, но стоит учитывать эти особенности в реальных проектах с жёсткими сроками или большим количеством джуниор-разработчиков.
Большую часть времени вы, вероятно, типизируете свойства компонентов, состояния и т.д. Однако всегда наступает тот день, когда приходится сделать что-то немного ненадёжное.
Дополнительные конфигурации для управления
Настройка типизированной экосистемы не так проста. Вы будете использовать инструменты сборки, управлять файлами конфигурации, создавать больше зависимостей и т.д.
Библиотеки, которые ранее легко интегрировались, теперь будут обращать ваше внимание на отдельные разделы просто чтобы наладить их работу с TypeScript.
Вы будете проводить всё больше времени в поисках решений на GitHub, пытаясь “подружить” Библиотеку Z с Flow или TypeScript.
Что ж, головная боль обеспечена.
Возможно, всё, что вам нужно, — это понятная IDE
IntelliSense в VSCode в комбинации с инструментами статического анализа кода предоставляют множество преимуществ, которые разработчики ищут в TypeScript и Flow.
Серьёзно, посмотрите, как далеко вы продвинулись, просто используя чистый IDE . Спорим, вы будете удивлены, как мало скучаете по TypeScript (но вы всегда будете чуточку тосковать по нему).
В конце концов, JavaScript свободно типизирован по своему дизайну, что даёт вам возможность использовать его на полную мощность и сосредоточиться на написании кода, не отвлекаясь на обеспечение безопасной типизации.
Заключение
Проект, написанный без типизации, пригодный для навигации и самодокументирования, является большим достижением. Может показаться, что я не люблю TypeScript, но это не так. Я люблю TypeScript и признаю его преимущества. Тем не менее важно обсуждать недостатки.
В конце концов речь идёт об использовании наилучшего инструмента для работы, верно?
Читайте также:
- Чистый код JavaScript: обработка ошибок
- Да не нужен вам фреймворк JavaScript!
- Отображение нативных всплывающих окон с помощью API уведомлений JavaScript
Перевод статьи Kris Guzman: Please Put TypeScript Down for a Moment