Pull to refresh

Comments 108

Если честно, лично для меня вся радость Typescript - это просто статическая проверка на этапе компиляции. Хорошо, я попытался воткнуть стрингу в номер. Ай яй яй. Нехорошо.

Но когда начинаешь его использовать, особенно когда ты парсишь казалось-бы банальный JSON, то начинается такая свистопляска. Например, мой код ожидает что в поле бла у нас лежит номер. Поднимаем JSON с диска, парсим его в тип бала, в котором у нас есть поле бла, а на диске у нас лежала стринга. Что мы получаем? Мы получаем полу-статическую типизацию.

Более того, даже самая, казалось бы нативная среда разработки для ТС - VSCode представляет собой непонятное нечто. Ошибки при компиляции представляют собой плащеницы. Мне надо минут 10 каждый раз, чтобы понять, какое поле я пропустил в каком-то типе.

В итоге, что?

Мы получаем как-бы ООП в функциональном языке, которое к тому же надо будет пропустить через мясорубку компилятора, который ещё и сделает из этого нечитаемый яваскрипт, который потом выплёвывает код, который как-бы строго типизирован.

Плюс, все эти замечательные линтеры, которые надо сидеть и отключать в те моменты, когда вам реально надо получить нетипизированые данные. Да, блин, я знаю, что я пытаюсь поместить объект типа unknown в функцию, которая проверяет его тип. Да, мне уже приходится использовать кодогенераторы, чтобы писать код проверки на соотвествие типам.

В итоге, мне кажется, что я трачу больше времени на обслуживание компилятора Typescript и Eslint, чем на разработку ПО.

Да, многое так. Авторы библиотек часто уходят с TypeScript на JavaScript, потому что так банально проще отлаживать и копаться в исходниках, в том числе и в node_modules. Но, всё же, JSDoc и/или .d.ts пишут, потому что ловить undefined is not a function и очепятки не хочется :)

JSDoc вполне себе достойная полумера для облегчения кодинга. Closure Compiler вроде даже умел на основе этих аннотаций делать статическую проверку. И зачем тогда эти лютейшие генерики и костыли?

Поднимаем JSON с диска, парсим его в тип бала, в котором у нас есть поле бла, а на диске у нас лежала стринга. Что мы получаем? Мы получаем полу-статическую типизацию.

К сожалению это следствие того, что JSON.parse возвращает any, это легаси с тех времён, когда unknown ещё не придумали. Если б там был unknown, компилятор от вас потребовал бы :)

Да вот в том-то и прикол, что как бы, ожидаемо - самая востребованная функция языка ею не является. Проверить что у меня переменная меняет тип можно и статиком. А вот для того, чтобы загружать JSON надо либо тянуть какие-то дополнительные либы, либо писать тайпгарды напрао и налево. Пока что я пишу тайпгарды.

Просто прикол в том, что если я, ни дай Боже-Бог и не искуси меня Дьявол, напишу any в своём коде, просто по приколу, чтобы выплюнуть переменную в логи и посмотреть на неё, то на меня будет сниспослано пять тысячь великих ворнингов, которые будут прикрыты ещё двумя тысячами ошибок.

Зато, если мне в строгий тип случайно залетает что-то левое, то язык это проглатывает за милую душу, и притворяется, что ничего не происходит.

В итоге - 80% моего кода - это проверка на undefined.

Как будто в базовом JS не надо проверять на undefined...

Да и TS не один такой, где переменную с типом, аналогичным any, надо проверять на пустое значение: в C это проверка указателей на NULL (а потом ещё и приведение к ожидаемому, а не фактическому типу), в C++ - на nullptr, в Python - проверка на None, в Go - на nil, и так далее. Даже с более мощными, полноценными алгебраическими системами типов, всё равно надо проверять Option на Some(x) / None в случае Rust или Maybe на Just x / Nothing в случае Haskell, хоть там это и зачастую удобнее, чем может предложить if.

О, нет, я на самом деле не undefined боюсь. Благо в JS этот костыль можно сделать просто через if(variable), а в TS приходится дописывать, что ещё тебе для счастья надо.

Я говорю о том, что вы запросто можете обнаружить string там где вы по своему типу ожидаете int. И это не вызовет никаких противоречий в среде исполнения. Эта типизация - только для статической проверки кода. Но такую уже проще сделать на самом JS.

Если по логике в number может оказаться и string то тип поля надо определить как "number | string", в чём проблема?

А в чем проблема maybe проверять на nothing? Это же не легаси, это фича для удобства. Если nothing не нужен, то и maybe не требуется. Используешь просто тип и ничего левого компилятор не пропустит.

Проблемы нет, просто хотелось напомнить, что если переменная (точнее, её тип) допускает значение, равное "ничего", то без проверки на это самое "ничего" не обойтись, независимо от языка и строгости/мощности его системы типов.

Кстати, очень интересно наблюдать за С-программистами, которые изучают алгебраические типы, и осознают, что type* - это фактически сумма NULL и non_null_ptr<type*>.

P.S. Зачем в JS, в отличие от большинства других языков, есть аж два разных "ничего" ( "ничего потому что ничего" =undefined и "ничего потому что так надо" =null) - отдельный интересный разговор.

допускает значение, равное "ничего", то без проверки на это самое "ничего" не обойтись

Вы путаете причину и следствие. В строгих языках переменная допускает ничего, потому что это по логике программы нужно. Не случайно, из-за чего "не обойтись" без проверки, а наоборот - логика этого требует, поэтому вы специально заводите переменную, которая "ничего" допускает.

Там, где по логике вам "ничего" не нужно, вы такую переменную не заводите и проверка не нужна.

Имхо, если бы JSON.parse возвращал unknown, было бы неудобно работать. По аналогии: массив может вернуть undefined при обращении по несуществующему индексу, и по-честному нужно было бы делать тип возвращаемого значения T | undefined, но проверок будет больше чем осмысленного кода.

noUncheckedIndexedAccess.
Но да, это хороший аргумент. Собственно разработчики TS постоянно ищут подобный баланс в различных вещах - где например, as const появился именно потому, что по-умолчанию типы не остаются литеральными (а могли бы!) и расширяются по ряду интуитивных правил. Например, тип значения 'foobar' мог бы по-умолчанию выводиться как 'foobar', но это было бы непрактично, поэтому он расширяется до string.

Поднимаем JSON с диска, парсим его в тип бала, в котором у нас есть поле бла, а на диске у нас лежала стринга. Что мы получаем? Мы получаем полу-статическую типизацию.

Ну используйте статически типизированный парсер файлов с диска.

А почему бы просто не взять обыкновенный JS? В данном случае, как раз автор оригинальной статьи и прав. Если там на диске такое всё кучерявое, то какой смысл писать на TS?

Чтобы привести это самое «кучерявое» в нормальный строго типизированный вид, например? Я не очень представляю, как можно работать с данными, имеющими тип «хрен знает что там»

Забавная рекурсия получается. Вроде же TS позиционируется как лекарство как раз для "работы с этим самым кучерявым".

Я вот не очень представляю как вместо номера может "нечаянно" прийти строка... Если я ожидаю номер - я и получу номер. Так что перманентный бардак с типами обычно как раз у адептов TS. Вначале разводим бардак с типами, а потом лечим следствие, а не проблему))

Значит вы не работали с реальными данными в реальных проектах. Прилёт номера вместо строки - вполне обычное явление, и перед работой нужно проверить, что номер является номером, и typescript заставит это проверить (если не лепить any куда попало) - чем он и прекрасен

В 100500 раз слышу эту мантру...

Ну т.е. вы расписываетесь в том, что в коде у вас полнейший бардак? Который вы красиво именуете "обычное явление"))

Даже если там данные отправляет какая-то машина, живущая сама по себе, в чём проблема по дефолту приводить к строке?

"Вы просто не работали с очень крупными проектами"

Вот да, у меня есть несколько некрупных проектов, которые с годами случайно вырасли в крупные, и теперь вот сижу и думаю, как их рефакторить, потому что без аннотаций типов на код теперь даже дышать страшно - совершенно непонятно, что откуда прилетает и всегда ли оно прилетает, куда улетает и какие типы ожидает

Поработаете в крупных проектах без typescript - потом прибежите в эту ветку плакаться, что как же я был прав :)

Ой, там кто-то выше расстроился, что я подобную аргументацию уже предупредил? Ну мне несложно повторить))

Пока что "крупные проекты" меня вымораживают своей необходимостью на каждый чих проставлять типы. Отличная оптимизация рабочего процесса!

что в коде у вас полнейший бардак

Изначально речь не о коде, а о ВНЕШНИХ данных, которые я НЕ контролирую

в чём проблема по дефолту приводить к строке?

В том, что мне нужна не строка, а число

Речь не о коде, а о ВНЕШНИХ данных, которые я НЕ контролирую

Это всё и есть кодовая база. Это же один проект?))

Вообще как это получается? На выходе бэка у вас абракадабра, на входе фронта всё разложено по полочкам? Т.е. вот это "нормальная практика"? Т.е. весь хайп вокруг TS основан на том, что бэкенд, со всеми его "строго типизированными ЯПами" не может корректно типизацию провести, а фронтенд с "динамически типизированным JS" может? Мы в этой логической цепочке ничего не перепутали? Может каждый будет заниматься своими обязанностями?

В том, что мне нужна не строка, а число

Ну приводите к числу))

И яснее объясняйте заодно:

Прилёт номера вместо строки - вполне обычное явление

Это же один проект?))

Нет, сторонние компании, с которыми я обмениваюсь данными, никак не относятся к проекту (и некоторые даже не знают о существовании моего проекта)

На выходе бэка у вас абракадабра

С чего вы вообще взяли, что речь о бэке и фронте? Ещё раз, речь о ВНЕШНИХ данных, которые вообще не мои и создаются не мной

Ну приводите к числу))

"Две книги по [object Object] рублей, итого NaN рублей"? Спасибо, не надо

"Две книги по [object Object] рублей, итого NaN рублей"? Спасибо, не надо

Как будто вам TS тут волшебным образом нужные данные подставит))

По крайней мере он хотя бы не позволит внезапно заменить число на строку:

let userCartSum = 0;
const bookPrice = "9"; // Бэкенд, бессердечная ты сволочь
userCartSum += bookPrice + bookPrice;  // Не "99", а не скомпилится

Если вы боитесь такой подлянки от бэка, что мешает сделать так:

let bookPriceNumber = Number(bookPrice)
if(isNaN(bookPrice)) console.warn("Алярм! Пришла какая-то лабуда вместо цены!")

Ну а впрочем ситуацию с бэком и фронтом тоже можно рассмотреть. Вот допустим изначально с бэка прилетает информация об авторе поста в виде строки с никнеймом, но потом бэк решает добавить больше информации и меняет строку на объект с инфой (аватарка, карма и т. п.), нарушая обратную совместимость. Как, не проверяя типы по всей кодовой базе, разработчик фронта может быть уверен, что он не забыл поменять условное author на author.nickname везде где надо? Компенсировать отсутствие типов 100% покрытием тестами или есть более адекватные варианты?

Хорошее замечание про тесты.
Строгость типизации - это такая прямая. С одного конца динамические языки, где именно что нужно покрывать код тестами в том числе на адекватность входа и реакцию на него. Посередине - языки вроде TS, где часть проверок корректности уезжает в compile-time. С другого конца - языки вроде Idris, где типизация настолько мощная, что код пишешь по принципу "компилируется - значит корректен". Unit-тестов ещё меньше, важнее становятся всякие e2e и интеграционные.

Хотя я сейчас понял, что продолбался с примером и от неявного преобразования объекта в строку TypeScript всё равно не спасёт:

type Author = { nickname: string };
const author: Author = { nickname: 'admin' };
console.log(`Автор поста - ${author}`);
// Автор поста - [object Object]

Однако обычный JavaScript от такого тоже всё равно не спасёт)

Впрочем, можно прикрутить какой-нибудь плагин к какому-нибудь линтеру, я надеюсь?

 что он не забыл поменять условное author на author.nickname везде где надо

Во-первых, почему бэк добавляет одну фичу в одном месте, а я должен лопатить после этого весь код?

Во-вторых, код вам и так придётся весь лопатить - меняя строку на объект во всех местах, где она встречается. Но опять-таки - это вопросы уже к архитектуре. Если типовая операция по расширению функционала превращается в такое болото с поиском по всей кодовой базе.

В-третьих, вместо одной строки может прийти другая строка - TS это спокойно схавает. И уверенности в корректной работоспособности приложения вам это не прибавит. Тут надо именно понимать - что откуда куда приходит и куда уходит))

меняя строку на объект во всех местах, где она встречается.

Но TypeScript или иной тайпчекер заботливо высыпет мне список всех мест, где это нужно сделать, и мне не придётся искать их вручную (правда, места с неявными преобразованиями в строку всё равно создают проблему, это печально)

с поиском по всей кодовой базе

Опять же — искать буду не я, а TypeScript

вместо одной строки может прийти другая строка - TS это спокойно схавает.

Поэтому особо упоротые по типам идут ещё дальше и заводят по отдельному типу для каждого отдельного вида строк (и для каждого отдельного вида чисел, чтобы нельзя было случайно сложить килограммы с метрами и так далее) — но это уже другой уровень, которого лично я пока ещё не достиг

Во-вторых, код вам и так придётся весь лопатить

и в ванильном js чёрта с два у меня отработает "find usages".

Ну т.е. вы расписываетесь в том, что в коде у вас полнейший бардак? Который вы красиво именуете "обычное явление"))

Если у вас в проекте 200 человек, как может не быть бардака? Они все разные, у них разный уровень подготовки, разный опыт. Ещё они постоянно увольняются (в айтишечке же если больше двух лет работаешь на одном месте - значит всё, деградируешь. Бред кмк, но люди же так и скачут по компаниям), приходят новые. Бардака не может не быть, если за этим не следить.
Вот TS - и есть средство слежения. Никто не говорил, что он очень нужен в проектах, где 5 или даже 15 человек. Только как раз таки все деньги крутятся в тех компаниях, где ПО сложное, и его разрабатывает много людей.

Никто не говорил, что он очень нужен в проектах, где 5 или даже 15 человек.

А потом однажды обнаруживается, что в команде уже 200 человек, из которых никто не может ответить, author это строка или объект (или, может, вообще числовой id, а может разные варианты в зависимости от контекста), потому что программист, создававший этого authorа, уволился ещё пять лет назад, и в итоге команда сидит и ищет, откуда этот author в принципе может прилететь в пределах всей кодовой базы. Так что, имхо, лучше с самого начала делать всё с аннотациями

А потом однажды обнаруживается, что в команде уже 200 человек, из которых никто не может ответить

Вот! Вот в том-то и суть, что по мере роста проекта нужно переходить от договорённостей на словах и в тексте (если таковые вообще есть) к договорённостям, проверяемым автоматически. Код - всегда основной источник правды, а то, что у каждого разработчика в голове - это представление о небольшой части кода проекта и его поведении. Поэтому реально полезно лишь то, что записано в коде, автоматизировано скриптами или изложено хотя бы в человекочитаемом тексте. Остальное - воздух, который уйдёт вместе с вами, когда через полгода вам сделают очень выгодное предложение о работе :)

Я бы тут говорил о легаси системах. В коде может быть и бардак, но переписывать его не будут. Пусть стоит.

Но прикол вот в чём. Только что дебажил систему. Фронт на реакте с TS. Бэк на голанге. Голанг ожидает от фронта набор строк ключ-значение. Внезапно после добавления нового параметра бэк бомбит ошибками.

Открываю логи - смотрю. Несчастный голанг ожидал строки. А я ему в json прислал число! Тут голанг и бомбануло. Пришлось добавлять toString() на стороне морды в TS.

Вот такого поведения я ожидаю от строго типизированого языка. А не этих прыжков через обручь с насвистыванием гимна Америки.

Реально, а чем тогда эти 200 человек заняты, что они понятия не имеют с чем они работают? Что за код они там пишут? Они же как-то должны манипулировать этими данными, а они вообще не в зуб ногой - что это за данные? Линтер ублажают? Я искренне не понимаю как можно писать код в таких условиях.

Реально, а чем тогда эти 200 человек заняты, что они понятия не имеют с чем они работают?

А кто говорил, что они понятия не имеют? Эти 200 человек - они же не археологи, и не смотрят на код, написанный на древнем пергаменте, пытаясь расшифровать древний язык программирования.
Они меняют этот код каждый день. Пока 10 человек читают код функции f, один человек его меняет, или вообще удаляет эту функцию, заменяя все её вызовы на что-то другое.
Если 200 человек будут сообщать друг другу о том, что они делают каждый день, то они только и будут этим заниматься, а не писать код. Рано или поздно проект достигает такого размера, что отдельно взятый разработчик не может читать/знать весь остальной код. Для этого придумали декомпозицию и программные интерфейсы, и 1000 и 1 способ их декларирования и соблюдения. TypeScript - один из них.

Мы каждый рабочий день кроме пятницы релизим примерно по 20 задач, начиная от небольших фиксов и заканчивая бранчами на 2000+ строк изменений. Если в одном таком бранче один разработчик вызовет функцию f, а другой, в ДРУГОМ бранче, удалит её (не подозревая, что она кому-то понадобится), то что будет, если эти задачи попадут друг за другом в мастер? Правильно, ничего хорошего. И с JS мы узнаем об этом только в рантайме. А весь продукт перед релизом каждый день проверять невозможно.

При работе с чужими данными, а файл на диске - это чужие данные, описанная вами проблема точно также возникнет и в C++ и в Java. Внешние данные всегда требуется проверять и на типы и на валидность. Особенно тут отличился один андроидовский парсер, который со стандартными настройками кидал исключение при появлении нового поля в JSON. А андроидовское приложение - это не фронт на в браузере, его просто так не обновишь.

Странная логика. Если вы ожидаете номер и получаете номер, то типизацию вы вообще не заметите. Можно добавить и всё продолжит работать, как работает. Если типизация что-то ломает, значит и был бардак с типами.

Если я ожидаю номер - то я именно сделаю так чтобы пришёл номер ;) Приведу к числу по дефолту, если не приводится - точно так же выведу в консоль ошибку (вернее даже сразу определю поведение программы). И сделаю я это только в тех местах, где потенциально может быть неразбериха с этими типами.

И сделаю я это только в тех местах, где потенциально может быть неразбериха с этими типами.

То есть доверитесь человеческому фактору, который может что-то упустить, вместо бездушной машины, которая гарантированно и неотвратимо проверит вообще всё и везде? Не понимаю я такой мазохизм...

Чуть ниже ответил. Добавлю ещё от себя - обязательно сообщит. В 100500 раз, что очередная сущность не типизирована или об "уязвимости в виде :any"))

Вот где мазохизм!))

сделаю я это только в тех местах, где потенциально может быть неразбериха с этими типами.

Человек, конечно, мудрее компилятора. Вот только брать на себя его работу вы будете, пока не лень. Через какое-то время наиграетесь. А компилятор не устает никогда, в этом преимущество.

Но это в любом случае полумеры. Не понимаю я такого шаблонного программирования. "уяк-уяк и в продакшен", а если что не так, компилятор сообщит. Или не сообщит...

Я вот не очень представляю как вместо номера может "нечаянно" прийти строка... Если я ожидаю номер - я и получу номер. Так что перманентный бардак с типами обычно как раз у адептов TS

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

И всё это надо поддерживать, интерфейсы иногда меняются со временем. Т.е. нельзя в какой-то момент "развести бардак с типами", а потом жить-поживать. Процесс непрерывный.

Да хватит уже расписывать - понял я, понял. Там у вас какие-то совершенно непостижимые вещи происходят. Только ваша задача как раз с этими данными некие манипуляции производить, но если уж вы даже понятия не имеет что это за данные...

Вроде тайпскрипт позиционируется как лекарство от кучерявого.
То есть понятно, есть проблемы с входными данными, но после этого ты получаешь поведение, которому можно доверять.

То есть понятно, есть проблемы с входными данными, но после этого ты получаешь поведение, которому можно доверять.

В том-то и дело, что нет. Пришла строка вместо числа - компилятор плюнул ошибкой в консоль. Всё. Проект все равно не функционален. А уж если у вас вместо одного числа пришло другое (а согласитесь, при таком бардаке в проекте - удивляться этому не приходится) - никакой TS не поможет.

Если компилятор плюнул — это замечательно, потому что такую ошибку быстро, легко и дёшево исправить, и она даже в коммит в репозитории не успеет попасть.

Если какая-то проверялка типов выплюнула ошибку в рантайме из-за того, что бэкенд внезапно прислал вместо числа строку — чуть менее приятно, но по крайней мере очевидно, в каком конкретно месте испортился тип и где это исправлять.

Когда толпа пользователей прибегает жаловаться, что две книги по 9 в сумме стоят 99 — это катастрофа: техподдержка перегружена, чудовищный репутационный ущерб, и нужно срочно как-то найти, в каком конкретно месте число превратилось в строку (совсем не факт, что виноват именно бэк), и исправить желательно вчера

Это, видимо, какое-то новая парадигма программирования. Или я слишком стар стал уже для всего этого...

Вы рассказываете про "ОЧЕНЬ крупные проекты" и одновременно с этим у вас в продакшен вываливается подобное... Где тестовое окружение? Чем занят QA-отдел?

Если какая-то проверялка типов выплюнула ошибку в рантайме из-за того, что бэкенд внезапно прислал вместо числа строку — чуть менее приятно, но по крайней мере очевидно, в каком конкретно месте испортился тип и где это исправлять.

Вот этого я искренне не понмиаю Это как бы бэкенд должен делать и фиксить! Ну найдёте вы по быстрому что именно прилетает "не то". Дальше-то что?

Я не пойму прикола - это бэкендеры что ли TS так пиарят?))

Это как бы бэкенд должен делать и фиксить!

Почему? Это может быть осознанное и задокументированное изменение в API, и задача фронтенда — научиться работать с этим изменением.

Собственно, наивность и рейтинг ваших комментариев ещё раз демонстрируют, что с реальными проектами вы не особо работали)

Вы рассказываете про "ОЧЕНЬ крупные проекты" и одновременно с этим у вас в продакшен вываливается подобное... Где тестовое окружение? Чем занят QA-отдел?

Допустим вы релизитесь каждый день, ну или хотя бы раз в неделю (что считается редким на сегодняшний день для технологичного бизнеса). Вы каждый день будете проверять вручную весь продукт?
Где тесты, спросите вы? Ну так TypeScript это и есть в каком-то смысле формат таких тестов, просто очень глубоко встроен в код. Код не нужно запускать даже для базового "тестирования" с помощью TS.
По сути где больше компилятора и его строгости, там нужно меньше тестов, и наоборот.

база. Больше строгости - меньше багов, меньше тестов. Еще и IDE начинает подсказывать полезные вещи, а не что попало)

"Зачем вы автотесты? Просто пишите без багов." (c)

Поднимаем JSON с диска, парсим его в тип бала, в котором у нас есть поле бла, а на диске у нас лежала стринга. Что мы получаем? Мы получаем полу-статическую типизацию.

Если у вас интеджер ожидается в json то везде в коде вы его ожидаете. По опыту в js нердеко он становится потом стрингой и никото незамечает. По-моему процентов 30 багов связано с этим(исклячая ошибки бизнеса)
Если мне надо поменять тип поля или удалить его компилятор(ИДЕ) сразу покажет все места его использования.
Если мне через год надо понять что в коде происходит то мне ненадо перерывать все доки чтобы узнать а чот же там моежт быть и какие еще поля есть в это json.

В итоге, мне кажется, что я трачу больше времени на обслуживание компилятора Typescript и Eslint, чем на разработку ПО.

Просто пишу код, компилятор выкидывает ошибки только если я где-то ошибся. Непонмню отдельных приседаний для компилятора.

Просто пишу код, компилятор выкидывает ошибки только если я где-то ошибся. Непонмню отдельных приседаний для компилятора.

Потому что эти "приседания" (и ещё кучу всего в придачу) уже отработал линтер. Только попробуйте таб вместо пробелов влепить или кавычки не того сорта))

в новом веб шторме поравили проблему с ошибками теперь они выводятся кросивым и понятным деревом. для вс кода тоже плагин есть

Что же, прощай, TypeScript. Пусть ты принесешь много строгости и удовлетворения своему племени, позволяя остальным наслаждаться JavaScript в том славном духе, в котором он был изначально задуман: Свободным от сильной (строгой) типизации.

Не знаю, в каком там духе был задуман JavaScript, по-моему его вообще не задумывали писать больше 100 строк кода в одном приложении. Это теперь его толковые люди пытаются дотянуть до приличного уровня, как улучшением самого ECMAScript, так и новыми языками. Я не могу относится серьёзно к языку, в котором до сих пор нет нормальной возможности создать композитный тип с equality-by-value, т.е. запись.

Ну а вообще, я думаю всё зависит от ожиданий от языка и вообще от написания кода. Вот допустим для чего лично я пишу код? Что я ожидаю от результата? Я ожидаю, что путём вложения большого количества времени и путём итеративных улучшений смогу создать систему из большого количества элементов, большинство из которых у меня в голове не могут поместиться за один раз. Но благодаря типам, модулям, проверкам во время компиляции и т.д. я могу с помощью ВРЕМЕНИ и УПОРСТВА (труда) повысить КАЧЕСТВО моего результата. Поэтому я работаю в проектах, где нужно достаточно высокое качество. Я люблю надёжные системы, мне неинтересно писать быстро и на "сойдёт", я уже старый для этого, пора создавать надёжный и полезный код, пара мгновений - и ты уже пенсионер, и слишком медленно тормозишь.

Другие же люди создают стартапы за пару недель, им завтра нужен PoC, а послезавтра - работающая страница оплаты, чтобы брать денежку. И я не могу их в этом винить, они строят бизнес, возможно они завтра будут менять мир вокруг меня. Нужны ли им пляски с типами и с ошибками компиляции? Наверное нет.

Всмысле нет? За час набросал код. ТС ткнул меня носом, где я ошибся. Быстро пофиксил и зарелизил. Без ТС я бы ещё сутки дебажил в поисках глупой опечатки.

Абсолютно согласен, TS ускоряет разработку. Если кому-то кажется что на JS быстрее то это лишь за счет следующей недели разработки, когда всё станет намного медленнее. А значит можно приблизительно оценить объем памяти размеры проектов постоянных любителей JS

Статья в одном предложении: "Любителю Ruby не понравился Typescript". С учетом философии этих языков, я был бы удивлен услышать обратное.


По поводу жалоб на "полу-статическую типизацию" — обычно так выходит, если человек приходит со знанием языка X и ждет, что в TS поведение некой фичи будет точно таким же, а оно оказывается иным. Конечно, программист на фортране может на любом языке писать на фортране, но будет ожидаемо больно. Не надо так!


В TS действительно многие вещи работают непривычно. Дело в том, что у него изначально была уникальная задача: попытаться обуздать семантику абсолютно безумного языка, коим является JS. И решили ее, конечно, не без отдельных косяков, но в целом очень неплохо.


Конкретно для проблемы из комментов выше: если вы работаете со сторонним бэкендом, который вы не контролируете, или с произвольными файлами с диска, которые кто-то мог попячить, то есть чудесная библиотека для контроля типов в рантайме:


https://github.com/gcanti/io-ts

Ух ты, спасибо, любопытно. Пока на поверку выглядит, как Zod :)

Почитал первый комментарий, сразу захотел написать про zod, про который, комментатор, видимо, не слышал.

абсолютно безумного языка, коим является JS

Вполне нормальный язык для своих задач. Просто не надо на него навешивать задачи, под решение которых он не задумывался.

Например, сортировку массива чисел по значению, а не лексикографчески :)

Гы-гы, а ещё он числа складывать не умеет - 2 + '2' = '22'

Задумывался он под написание однострочников для DHTML, чтобы снежинки на фоне летели и часы за мышкой бегали. Но теперь задачи другие, а решать их так или иначе приходится на инструментарии, построенном на тех же старых принципах

Но теперь задачи другие

Может проблему стоит поискать тут? А то повадились бизнес-логику на клиенте считать))

Приятно сознавать, что ты не сошёл с ума и кто-то ещё (какой-то создатель какого-то Ruby on Rails) думает так же.


Единственное, что — в отличие от автора я не считаю ES очень хорошим языком. Строгости при работе с типами ему действительно не хватает. Но это должно решаться не необходимостью описывать типы, а более строгими правилами при работе со значениями. Дайте мне superstrict, при котором бы на попытку 2 + '3' (или любое другое несоответствие) вылетало исключение — и вот тогда я буду счастлив.

в Python

Вот я что-то не уверен, что в нем с этим строже.

2 * "2" == 4
2 * "2" == "22"

Угадаете, где какой язык? :)

Хм, ну тут же не приведение типов, а заложенная работа оператора * для удобного построения строки из N подстрок

Нет разницы, как это называть, если эффект аналогичный. Представьте себе умножение не двух литералов, а двух переменных. Которое где-то в рантайме начинает внезапно выдавать в 10 раз больший результат.

Добро пожаловать в Python или Ruby :)

К счастью, браузер, для которого я чаще всего пишу, уже поддерживает такой строгий скриптинг (и это не Python или Ruby).


Добавлю, что эта (и некоторые другие) особенность настолько важна для меня, что я даже готов пожертвовать кросс-браузерностью (такой код не получится запустить в CEF).


Общий тренд изменений в ES достаточно оптимистичен. Например, на первом месте в документации по strict mode стоит:


  1. Eliminates some JavaScript silent errors by changing them to throw errors.

Даст бог, в новых версиях ES и BaNaNa'вые фокусы пофиксят, потому, что в реальных проектах от них гораздо больше вреда, чем пользы.

Поддерживаю. Динамическая типизация должна быть строгой!

Дайте мне superstrict, при котором бы на попытку 2 + '3' (или любое другое несоответствие) вылетало исключение

И потом пытаться по стектрейсу какой-нибудь sum() понять, откуда именно в нее пришла строка вместо числа? Вместо того, чтобы IDE сразу, ещё до запуска кода подсветила место, где я при рефакторинге забыл поменять тип поля.

Я бы понял, если бы речь шла про Java. Про их километровые колстеки рассказывают анекдоты. Но ES? Размотать любое исключение там совершенно не проблема. Проблема в том, когда оно не происходит, а даёт неожиданный результат.


Что касается подсветки. Во-первых, предлагаю перечитать статью. Там объясняется, что это удобство совсем не бесплатно. Компиляция в человеческом пайплайне — это всегда высокая цена. Во-вторых, в реальности такие ошибки происходят отнюдь не по простым сценариям, которые можно было бы подсветить (как и в C/C++ — там есть компиляция, и ошибки, к сожалению, тоже есть). В-третьих, существуют тесты, их и надо использовать — а компиляция слишком слабый вид тестирования, который другие виды заменяют на все сто.

Но ES? Размотать любое исключение там совершенно не проблема.

Нет. То есть, да - вы, может быть, даже в половине случаев правы, а в случае пет-проектов - в подавляющем большинстве.

Но попробуйте размотать стектрейсы в Nest, который работает поверх Fastify, который работает поверх Deno, который работает поверх V8, когда выбивает абстрактную ошибку типа Unexpected end of form at Multipart._final in Multer в методе контроллера, который обклеен десятью декораторами (и ещё пятью декораторами-инжектами в конструкторе) и двумя интерцепторами. Абсурдность сей ошибки ещё и в том, что Multer юзается исключительно в Express, хотя у меня вместо Express под капотом Nest'а стоит Fastify.

Насколько помню, стектрейс ну тысячи на две вызовов состоял. Не Java, конечно, но такое дерьмо даже разбирать не хотелось. Я в итоге за два часа дебаггинга и анализа стектрейса не понял, кто в итоге выдал ошибку.

А виноват, кажется, был неправильно примененный декоратор в конструкторе класса контроллера, который почему-то в PUT-запросах с типом тела multipart/form-data импортировал этот Multer и пытался работать с моим проектом, как будто он запущен на Express. Кинул мейнтейнеру ишью, посмотрим, как будет...

Программист на Джаве на любом языке будет писать на Джаве )))


Я немного подумал, и мне теперь кажется, что тут проблема несколько сложнее, чем «динамическая типизация vs. статическая». Тут, скорее, конфликт методологий. И если брать тяжеловесную, где фреймворк фреймворком погоняет, очень может быть (я не знаю, опыта такого нет), что компиляция TypeScript'а действительно чем-то облегчает жизнь по сравнению с анализом исключения. А вот какой вывод из этого делать… Я такие системы не люблю, потому что считаю, что сложность в них недостаточно изолирована. Ну куда это годно, в самом деле: колстек на 100+ позиций? Для сравнения, в Windows-приложении он будет, максимум, в два десятка глубиной, а потом — ntdll.dll или kernel32.dll. Это ведь не говорит о том, что все Windows-приложения поголовно проще энтырпрайза, это говорит, что там изоляция лучше спроектирована.


Ну вот, и вывод я делаю, что динамическая типизация — это канарейка, которая первая сдохнет при таком (с моей точки зрения) неправильном подходе, то есть, очень полезное животное. Конечно, строгая динамическая типизация, а ES всё ещё страдает детскими болезнями типа склонности скрывать ошибки ради достижения хоть какого-то, пусть и непредсказуемого результата.

Размотать любое исключение там совершенно не проблема.

Проблема в том, что исключение вылетит, когда будет какая-то несуразная даже по меркам Javascript операция, а это может случиться сильнее позднее того момента, когда в приложение попали данные в неверном формате.

Это сложная ситуация, и она требует сложных тестов — таких, которые жизненный цикл данных покрывают полностью. Если такой тест не написать, а ограничиться только компиляцией, пострадает надёжность.

А потом через пару лет статья: "Turbo 12 переписали на TypeScript"

JavaScript — это язык необходимости (если не учитывать WebAssembly, прим. перевод.)

JavaScript остается необходимостью и с WebAssembly, хотя бы для того чтобы запустить его или манипулировать DOM

А можно пример использования фреймворка Turbo и какой-то учебник или хотя бы пример уровня TODO приложения, а то событие конечно инетресное, но может проект уровня домашнего баловства у какого-то там подгоревшего Ruby-ста. Да и Basecamp тоже не фонтан как-бы даже в свое время... Так что хотелось бы тестимониалсов для вразумительно убедительного доказательсвта, что автор не просто олень, а уважаемы дон и его мнению надо верить...

Вообще-то данные типизированы. Если ожидаешь число, должно приходить число. Что можно сделать с ценой товара, которая пришла в виде "бла-бла-ой-не-то"? Вывалиться в ошибку? Обмазать динамическими проверками? Какой смысл в динамике в самом принципе? Какой смысл в JSON рандомного формата? Системы обмениваются конкретными моделями с конкретными типами, а не по принципу "как фишка ляжет". Если же автор борется с типами, значит он делает что-то не так изначально. И ему ничего не поможет. Не ES6+, ни TS, ничего.

Такое обсуждение, будто это, - серьёзная тема, а не детская хотела автора.

Не холиввр "JS vs полноценные языки", а "я хочу сделать JS в проекте хуже". Typescript, в сути, - инструментарий вокруг JS, ставший подобием стандарта.

У разработчиков на руках все карты, хочешь, - используй типы, чистый JS в TS файлах, хочешь, - решай для каждой отдельной папки. Есть объективные причины не использовать TS, но искоренять его поддержку причин нет.

Прямо религиозное бинго:


  • Это детская хотелка, а не мнение!
  • JS без тупоскрипта неполноценный язык!
  • Это стандарт! Все 95% мух так делают!
  • Искоренять его причин нет, атвечаю.

Есть объективные причины не использовать TS

Ну да, ну да. Попробуй заяви о них, прибегают такие как вы и закидывают ка "сильными аргументами", обозначенными выше.

Ну так это, вперёд

А то слов мне в рот положить успели, а единственное утверждение не тронули

Edit: аж неправильно прочел, неужели вы буквально обвинили меня в том, что "такие как я" не поддержали бы.. Моё высказывание?

Большой проект без TS, десяток разработчиков. Не могу вспомнить, когда у меня в последний раз всплывала ошибка вида "подставлен String вместо Number, объект вместо скаляра". А если всплывает, то мгновенно видна при отладке или на крайний в юнит-тестах. И вот заставляют городить лишнюю тонну кода ради этих примитивных ошибок. Я какой-то особенный сверхвнимательный уникум, как и гонщик из статьи? (хах, я как раз гоночные игры люблю). Или это люди, которые боготворят TS, очень неуверенно чувствуют себя в JavaScript или имеют проблемы с концентрацией? Джуны; посредственные мидлы; люди, пересевшие с типизированных языков; просто ненавидящие JS, но пересевшие на него, т.к. "тут золота больше".


Один плюс TS для меня неоспорим — code completion. Но за год работы над текущим проектом уже въелись в подкорку сигнатуры часто используемых функций. Как и "родные" JS-функции за годы работы с ним. Если надо использовать более редкую функцию, в любом случае иду отдельно в документацию перечитывать.

Ох уж эта проверка типов юнит-тестами, чтобы "не городить груду кода"...

Вы по итогу свой личный опыт будете приводить как аргумент? Сухо и наивно


Во-первых, в моем комментарии ясно прослеживается тезис, - TS, - инструментарий, его использование гибко настраивается на уровне репы/папки/файла, мой аргумент соответственно, - нет причин бороться с инструментом, это как запретить открывать код в Idea, notepad only please.

На мой взгляд, основное преимущество использования TS, - самодокументируемый код и интегрированность разработки. То есть, более удобный способ наследования типов/аргументов, чем был бы в JSDoc.

Так же, как мне может быть удобно написать код на чистом, JS, я знаю, что расписывая экспорт функции, предназначенной для других людей, мне будет удобнее, если мне не придется расписывать JSDoc, им не придется читать исходники, а платформенная библиотека легко интегрируется с любым количеством промежуточных слоев без необходимости для меня клонировать чужой код, собирать проект, запускать тесты/курить CI и самое главное, отвечать на письма джунов.

Соответственно, во-вторых, вы забыли, что не все сидят на одном проекте годами, в особенности, когда вы начнете называть себя тех-лидом, а к "джунам и посредственным мидлам" прибавите "ленивых синьоров". Возможно вы поймете, что в большом проекте может быть и 100 человек, на уровень которых вы не сможете повлиять, а проектов, начатых не вами, может стать с десяток.


В-третьих, с чего вы взяли, что всем нравятся юниты? Особенно их читать? И особенно их писать "чтобы не городить код"? Запускать код и тесты, чтобы проверить, работает ли он, я может и так знаю, что работает.

Гонору много, имхо, аргументируете как джун

Вы по итогу свой личный опыт будете приводить как аргумент? Сухо и наивно

Я-то думала, личный опыт перевешивает все теоретические жонглирования и ссылки на опыт "кого-то там".


более удобный способ наследования типов/аргументов
мне будет удобнее, если мне не придется расписывать JSDoc
с чего вы взяли, что всем нравятся юниты

Ваши аргументы, если убрать воду, тоже сводятся к "мне удобно"/"другим удобно"/"не всем нравится".


платформенная библиотека легко интегрируется с любым количеством промежуточных слоев без необходимости для меня клонировать чужой код, собирать проект, запускать тесты/курить CI и самое главное, отвечать на письма джунов.

Водянисто. Почему без TS невозможно то же самое?


нет причин бороться с инструментом

Может, он так хочет отсеять неуверенных программистов на языке jQuery TypeScript. Да и вообще, переть против системы всегда было интересно.


аргументируете как джун

Зато хоть вас научили аргументировать получше джуна из первого комментария.

Ваши аргументы, если убрать воду, тоже сводятся к "мне удобно"/"другим удобно"/"не всем нравится".

Надеялся, что сарказм будет легче читаться

плюсы в отсутсвии типов исключительно на уровне ощущений, а минусы уже на уровне инженерии. Хорошо что многие js разработчики смогли приобщиться к нормальным инструментам и концепциям, ну а некоторые люди просто не исправимы, в общем-то это их право :)

Как говорится - а теперь отрефакторите мне этот трёхлетний проект, который писало 6 человек, на чистом JS.

TS это когда ты делаешь что-то сложное и на долго, особенно если это NodeJS и падение будет аффектить сильнее кривого рендера. А ещё инструментарии IDE, которые с TS позволяют переименовывать, перемещать, менять структуру и как угодно танцевать с кодом и всё с гарантией обновится где нужно. А с JS гарантий нет и добро пожаловать в дебаггер.

А с входными данными и не тем JSON - во-первых надо валидировать все входные данные, какой бы язык ни был, везде это вызовет ошибку, только где-то на парсинге, а где-то глубже в логике. А во-вторых - а нету этого пункта, если первый соблюдать - проблема исчерпана. Ну и выше уже было про модели данных и прочее.

Легко если есть тестовое покрытие на типы )

Я долго проповедовал JS, но как только проект стал большой и я начал забывать какие типы данных должна принимать та или иная функция и что должна отдавать на выход, я сразу перешел на TS.

Особенно TS спасает, когда нужно иметь типы респонсов внешних апи различных сервисов, при JS ты как слепой котенок в такой ситуации.

Большой пыт написания крупных проектов на Typescript и Javascript + JSDoc, есть что сказать. Ксли кратко: они равны по возможностям, отличается лишь синтаксис. Как async/await и then/catch. А на "чистом" Javascript без JSDoc писать что-то сложное не советую, одного лишь автовывода типов мало. Откуда автовывод в Javascript? Подкидываем в проект jsconfig.json вместо tsconfig.json, не забываем включить побольше строгих правил. Все, ваш Javascript-код проверяется точно так же, как Typescript-код. Далее вместо двоеточий используем документационные комментарии с тегами, самые популярные будут @type, @typedef и @template. Полно и других классных тегов, например, чтобы делать поля приватными, перегружать функции. Читайте про JSDoc, многое пригодится и для тайпскриптунов, пишущих библиотеки. А еще вы можете делать compile-time проверки через запуск tsc с флагами, можете использовать все это дело в watch-режиме или в pre-commit git-хуке. Ну и конечно для библиотек будет полезно, если вы сгенерируете d.ts файлы через тот же tsc при публикации.

Sign up to leave a comment.

Articles