Проблема с TypeScript и его практиками
Большинство из вас, вероятно, считают, что TypeScript - это лучшая практика в наши дни. У этого есть некоторые преимущества, но есть и недостатки. Вот несколько мифов и проблем, связанных с этим.
Миф 1: Типизировано и безопасно
TypeScript либо типизирован, либо безопасен. Будучи типизированным языком, компилятор на 100% знает тип каждого отдельного значения, двоичное представление значения в памяти и способ доступа к нему. Transpiler/compiler typescript не может и не знает этой информации. Динамическая природа JavaScript/ECMAScript предотвращает это. Что на самом деле делает TypeScript, так это пытается вычислить эту информацию или позволяет вам указать ее, но у него нет способа узнать это на самом деле.
const value: number = randomInt();
// value can be anything, really!
Миф 2: Создает меньше ошибок
Вопреки тому, что говорят многие люди, TypeScript создает не меньше ошибок по сравнению с приложениями, написанными на JavaScript. Это не наше мнение, это исследование. В ходе исследования были проверены 15% самых популярных приложений на github. В исследовании в двух словах говорится следующее:
- Использование "any" положительно влияет на время устранения ошибок.
- Код TypeScript содержит столько же или больше ошибок, чем код JavaScript.
- Исправление ошибок в проектах, написанных на TypeScript, занимает больше времени.
Так что вы определенно не должны расстраиваться, если используете "any" или свободный режим в TypeScript.
Также важно понимать, что, даже если вы можете счесть код TypeScript более читабельным, разве это не означает, что он на самом деле более читабелен? Читаемый код по определению прост в изменении и понимании, поэтому, если исправление различных ошибок на одном языке программирования занимает больше времени, чем на другом, является ли это признаком того, что что-то не так в вашем понимании того, что такое читаемый код на самом деле?
PS. Мы знаем, что существуют другие исследования, которые говорят об обратном, но, как также отмечено в исследовательской статье, эти исследования либо сосредоточены только на нескольких проектах, либо на библиотечном коде. Это отличается от того, что большинство из нас делает в нашей повседневной жизни. Большинство из нас кодируют приложения, а не библиотеки.
Миф 3: Масштабируемость
Насколько хорошо масштабируется ваше приложение в отношении сложности кода, не имеет ничего общего с тем, что вы кодируете на JavaScript или TypeScript. Это просто реклама от Microsoft или любителя машинописи, если кто-то вам это скажет. Честно говоря, типы - наименее важная языковая функция, и даже называть ее функцией спорно. Даже исследовательские работы 1970-х годов отмечали это.
Вы слышали о слабом сцеплении? В большинстве случаев именно этот фактор будет определять, будет ли ваше приложение масштабироваться или нет. Это имеет много преимуществ, и некоторые люди используют разные слова, но смысл более или менее один и тот же: чем меньше программисту нужно знать, тем лучше.
Миф 4: Гарантия будущего
TypeScript никоим образом не является доказательством будущего. Во всех местах, где вы можете запустить TypeScript сегодня, вы на самом деле запускаете JavaScript? Есть предложение добавить некоторые типовые аннотации к языку, но если вы прочитаете все выпуски и даже заметки о собраниях, будет ли это понятно:
- Аннотации будут просто рассматриваться как комментарии. Вы никоим образом не можете доверять этому типу.
- Большая часть кода TypeScript никогда не будет работать, потому что в нем используются специфические для TypeScript функции.
- Это не дает никаких технических преимуществ.
- Многие люди выступают против этого предложения, некоторые из этих людей входят в сам комитет.
Вы помните HTTP 2 push? Chrome отказался от поддержки, потому что слишком мало веб-сайтов использовали ее. Таким образом, даже если бы он был стандартизирован (это огромное "если"), браузеры, вероятно, все равно не добавили бы его поддержку:
- Это не дает никаких технических преимуществ, скорее наоборот (больше байтов => медленный веб-сайт).
- Почти каждый веб-сайт использует пакеты, а те, кто этого не делает, почти наверняка будут использовать специфические функции TypeScript, которые в любом случае не поддерживаются.
Проблема 1: Стимулирование именованного экспорта
Почти каждый модуль должен экспортировать только одну вещь. Существует очень мало исключений из этого правила, например файлы конфигурации или файл, содержащий несколько констант. И если вы экспортируете только что-то одно, очевидным кандидатом является экспорт по умолчанию. Аргумент, который обычно приводят любители TypeScript, заключается в том, что это облегчает рефакторинг, но технически это невозможно.
Если вы используете экспорт по умолчанию, скрываете ли вы идентификатор/название экспортируемой вами вещи. Это по определению лучше, потому что устраняет зависимость, вам не нужно знать, какое имя имеет какая-либо вещь в файле, только интерфейс вещи, которую экспортирует файл. Это великолепно. Это означает меньшую связь и меньшую "потребность знать". Он придерживается твердых принципов и является отличным дизайном программного обеспечения. Это также побуждает разработчика давать отличные имена для импортированных модулей, и каждый файл имеет разные контексты, поэтому имеет смысл использовать разные имена/идентификаторы в зависимости от контекста.
Проблема, конечно, в том, и это главная причина, по которой некоторые разработчики предпочитают именованный экспорт, что заставляет разработчика присваивать каждому пакету имя при его импорте, поэтому разные файлы могут использовать разные имена:
import Foo from 'somewhere';
import Bar from 'somewhere';
Следствием этого является то, что если вы используете некоторые инструменты "refactoring", будет ли сложнее переименовать "экспорт по умолчанию", особенно в масштабах всего проекта. Оба файла 1 и 2, вероятно, по-прежнему будут использовать имена Foo и Bar даже после автоматического рефакторинга, например. И это здорово, потому что, надеюсь, есть веская причина, по которой эти имена были использованы в первую очередь. Некоторые люди забывают об этом.
Проблема 2: Не поддерживается импорт с помощью расширения .ts
ECMAScript/JavaScript импортирует модули с помощью спецификаторов импорта/зависимостей, и каждая платформа, которую мы знаем, преобразует спецификатор зависимости в некоторый путь или URL, который она может извлечь. Если прямое местоположение не может быть легко определено, должна ли платформа выполнить какой-либо обход (посетить множество файлов, прежде чем она "узнает"). Этот процесс медленный, но удивительно медленный, если он выполняется по сети. Это может быть исправлено, если разработчик предоставит более конкретные спецификаторы зависимостей, например, добавив расширение файла. Но есть проблема.
TypeScript не поддерживается, если вы добавляете расширение .ts. Он не переименовывает спецификаторы зависимостей. На самом деле это чрезвычайно странно:
- Команда TypeScript на 100% знает, что TypeScript изначально нигде в мире не поддерживается. Таким образом, вы не можете оценить файл, содержащий код TypeScript. Сначала он должен быть преобразован в JavaScript.
- Команда TypeScript на 100% знает, что расширение ".ts" является их собственным расширением файла, и весьма вероятно, что если кто-то захочет импортировать файл ".ts" из файла TypeScript, то этот файл, скорее всего, является файлом TypeScript.
- TypeScript transpiler сам перепишет весь код, почти в каждом случае будет иметь доступ ко всем зависимостям и создаст файлы ".js" по умолчанию.
Нет никакого оправдания тому, чтобы не поддерживать это.
Проблема 3: Поощряйте раздувание
Сообщество TypeScript поощряет разработчиков к некоторым действительно вредным привычкам, которые делают базы кода раздутыми. Примеры:
Чрезмерное использование строгого режима
Строгий режим не приводит к уменьшению количества ошибок и не делает его более читабельным. Конечно, вы можете в это верить, но наука утверждает, что это не так. Вы обязательно должны использовать "any", если это делает код короче и легче читаемым, но существует безумное убеждение, что это плохо. Это не так!
Чрезмерное использование объединение типов
Если ваша функция имеет несколько объявлений, используйте несколько функций. Есть причина, по которой parseFloat и parseInt - это разные функции, а не просто "parseNumber".
Чрезмерное использование типов
Вас должен волновать не тип, который имеет конкретное значение, а интерфейс, который имеет это значение. Вместо того, чтобы делать:
function getName(user: User): string {
return user.name;
}
если вы предпочитаете:
const getName = ({ name = '' } = {}) => name;
getName() // OK
getName(123) // OK
getName(true) // OK
getName(undefined) // OK
getName(Symbol('test')) // OK
getName(null) // TypeError, but this is the only case
То же самое применимо, если вы работаете с универсальными и вариативными аргументами:
const add = (...args) => args.reduce(((s, v) => s + v), args.shift());
Стиль кода называется защитным кодированием, и научно доказано, что это лучший метод среди языков программирования в целом вместе с тестами для фактического уменьшения количества ошибок и создания более надежного программного обеспечения. Это очень важный навык, которому нужно научиться. И да, TypeScript обнаружит, если вы вызовете "getName" с помощью чего-то другого, кроме объекта. Если этого не произойдет, проверьте свой tsconfig.
Oпровержение
Мы знаем, что в сообществе TypeScript есть много людей, которые настолько неуверенны в своих навыках программирования, что воспринимают любую критику в адрес языка программирования как личную. Пожалуйста, не делай этого. Мы не хотим заставлять вас прекращать использовать TypeScript. Используйте любое программирование, которое вы хотите. Просто хотим поделиться некоторыми фактами, которые, как мы знаем, непопулярны, но все же бесспорно правдивы.