Pull to refresh

Comments 121

К сожалению, в реальном мире не все так безоблачно. Например, для Redux надо писать много бойлерплейта, и с TS его становится только больше. Не у всех пакетов есть качественные тайпинги (потратил два дня, чтобы компилятор перестал ругаться на adal-angular (ну да эта либа отдельная песня)). tree-shaking для TS пока что вроде бы нет. Language server иногда чудит, например, в WebStorm и VS Code ругается на кучу ошибок в проекте, причем на разные, и такое ощущение, что игнорит половину tsconfig.json — а проект при этом успешно собирается.


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


Еще я обнаружил, что WebStorm подхватывает определения из *.d.ts, даже если весь проект на JS — достаточно упомянуть нужный тип в jsdoc, и интеллисенс сразу умнеет. Можно начать свой путь в TS с этого, немного облегчить себе жизнь на отдельно взятом проекте, потратив пару часов и описав хотя бы самые важные типы. То же касается и сторонних тайпингов, установленных через npm install @types/lodash --save-dev. VS Code, ЕМНИП, вообще ставит тайпинги автоматически в фоне прозрачно (куда-то в свою папку, не в проект).

WebStorm подхватывает определения из *.d.ts, даже если весь проект на JS

И потом не можешь перейти на нужный файл, попадая на *.d.ts вместо исходников

Это правда, есть такая проблема. Но в таком кейсе и меньше необходимость копаться в чужих исходниках.

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

теперь видна

Видно что-то мало напоминающее JS. Часто с пяток вариантов даже с разным количеством аргументов.
И потом не можешь перейти на нужный файл, попадая на *.d.ts вместо исходников
Представим, у нас реакт. Зачем лезть (без какой-то сверхестественной надобности) в его исходники? Разве тайпингов, просто описывающих интерфейс, не достаточно?
Видно что-то мало напоминающее JS
Почему в подсказке вообще должен быть код? IDE берет описанные типы, существующий jsdoc (комменты и описания) и собирает из них хинт, как умеет.
Часто с пяток вариантов даже с разным количеством аргументов.
Вы про перегрузки? Так это возможность TS, а не недостаток.
tree-shaking для TS пока что вроде бы нет

если tsc настроить на модули из es6, то tree-shaking работает как всегда – rollup там, или webpack2. Если немного упороться, то можно и через closure compiler прогонять.

Ну понятно, просто хотелось бы из коробки и нативненько.

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

Redux надо писать много бойлерплейта, и с TS его становится только больше
Согласен, однако, введение discriminated unions и свитчей по их ключам серьезно облегчает работу с пэйлоадами экшенов (из-за type narrowing), и это перевешивает весь необходимый бойлерплейт.
Не у всех пакетов есть качественные тайпинги
Ну это все за пару секунд затыкается простой наколенной .d.ts-кой, предназаченной как раз для таких затычек.
tree-shaking для TS пока что вроде бы нет.
Если указать в качестве модулей es6 и скормить дальше вебпаку 2, то и tree-shaking работает.
такое ощущение, что игнорит половину tsconfig.json
А вот это уже странно. У меня порой тоже такое бывает, но почти всегда из-за того, что либо include/exclude некорректный, либо подцепляются вложенные tsconfig.json
Можно начать свой путь в TS с этого
Именно так и внедряли, более того, по этим .d.ts можно через typedoc прекрасные доки сгенерить. Гораздо лучше всяких esdoc/jsdoc/documentationjs и прочего
Согласен, однако, введение discriminated unions и свитчей по их ключам серьезно облегчает работу с пэйлоадами экшенов (из-за type narrowing), и это перевешивает весь необходимый бойлерплейт.

Покажите? Я общем-то новичок в обоих технологиях и не очень представляю, что вы предлагаете.


Ну это все за пару секунд затыкается простой наколенной .d.ts-кой, предназаченной как раз для таких затычек.

За пару секунд? Ну зависит от сложности библиотеки, конечно...


Гораздо лучше всяких esdoc/jsdoc/documentationjs и прочего

Чем оно лучше «гораздо»? Как по мне, так у jsdoc не хуже выходит — типы указаны, ссылки проставлены, комментарий написан, что еще надо? Остальное не пробовал.

Покажите?
Ну, в общем виде это выглядит примерно так:
enum ActionType {
    Foo,
    Bar
}

type FooAction = {
    type: ActionType.Foo,
    payload: {
        foo: string
    }
}

type BarAction = {
    type: ActionType.Bar,
    payload: {
        bar: number
    }
}

type Action = FooAction | BarAction;

const reducer = (state = {}, action: Action) => {
    switch (action.type) {
        //here's gonna be some TS2 magic about type narrowing
        case ActionType.Foo: {
            const {foo} = action.payload; //foo is string because of ActionType.Foo tag in Action discriminated union
            break;
        }
        case ActionType.Bar: {
            const {bar} = action.payload; //bar is number because of the same tag but different value (still the same union)
            break;
        }
        default: {
            //you can match this union endlessly until you have the same key 'type'
        }
    }
}

То есть фишка становится в том, чтобы свести все возможные структурные типы с одним общим ключом (тэгом, потому-что такая штука по-другому зовется tagged union) в один юнион. Тогда можно через обычный свитч сформировать своего рода паттерн матчинг (привет ФП) по оставшейся структуре. Это нереально помогает, когда все типы экшенов (FSA со своим пэйлоадом) сведены в один тип со своим тэгом, и каждая ветка свитча в редьюсере, благодаря TS2.1, может вывести нужный тип этого пэйлоада.

За пару секунд?
На самом деле да. Как-то так:
//mock.d.ts
declare module 'foo' {
  const default_export: any;
  export default default_export;
  export const named_export: any;
}

Чем оно лучше «гораздо»?
Я лучше сразу сознаюсь, у них лучших доступный html-паблишер :)
Само собой, можно было бы для чего угодно сделать любой паблишер. Но проблема гораздо глубже. JSDoc, откровенно говоря, буксует на месте.
Esdoc — ну тут вообще эпично. Автор считает проект исключительно своим хобби, и не больше. Однако, активно пилится форк и надежда все-таки есть.
А есть прекрасная штука, которая «just works». (Хотя со сложным проектом все-таки придется чуть-чуть посидеть, поучить эту штуку конфигам, ибо капризна.)
Я буду крайне нечестен, если не уточню, что форк esdoc разрабатывается в другом закрытом репе, а этот я по ошибке упомянул по старой памяти.

Спасибо за развернутый ответ.


В моем текущем проекте (его начинали еще на 1.8) у нас вместо enum тонна констант типа const ACTION_TYPE_BAR: ACTION_TYPE_BAR = "ACTION_TYPE_BAR"; и естественно хочется выколоть себе глаза.


Что до документации, то признаюсь, jsdoc-ом какое-то время не пользовался и даже не знал, что там все так грустно. А вот это чем сгенерено, typedoc (я там честно копался, но как дока генерится, не нашел)?

вместо enum тонна констант
А в итоге все-равно этим кончится, так как enum'ы в ts числовые, ну, то есть значения транспайлятся в числа от нуля. Понятное дело action types на основе enum нельзя использовать, если абсолютно все типы экшенов в проекте не сведены в этот enum, иначе будут коллизии.
На самом деле, TS добавляет еще одну строчку на каждый экшн в этот несчастный файл с типами экшенов.
//names should be the same
export type FOO = 'FOO';
export const FOO = 'FOO';

Строковый литеральный тип для свитчей, так как ts не умеет матчить юнионы с чем-то кроме литеральных типов и enums в тэге.
А константа для замены числовых enums на реальные строки для сериализации, дебага и предотвращения коллизий.
Я уж не говорю про необходимость «дублирования» структуры пэйлоада:
type FOO = 'FOO'; //string literal
const FOO = 'FOO'; //string constant
type FooAction = {
  type: FOO, //string literal type FOO
  payload: {
    foo: string
  }
}
const foo = (foo: string): FooAction => ({
  type: FOO, //string constant
  payload: {
    foo
  }
});


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

А вот это чем сгенерено, typedoc
Ага, он самый

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

Если бы всё писали на TS изначально, можно было бы много чего сделать изящнее и лаконичнее :)

Терять кучу времени на изучение подводных камне. Так сильно надо ли?

По моему опыту, переход на TS значительно улучшил качество архитектуры и уменьшил количество неявных проблем в коде. Поэтому для меня — оправдался.
Нюансы: изначально я бекенд разработчик, когда стал заниматься JS было лет 8 опыта на C#, Java и C++. Это может быть одна из причин, что мозгу значительно проще прицепиться к хорошо знакомым абстракциям.
С другой стороны, я чаще всего работаю в fullstack командах, где народ потихоньку осваивает FE после многолетнего опыта в BE. И в этом случае плюс огромный.

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

Я понял почему Вам так понравилось это. Это позволило попасть в привычную среду сущностей.

Основное достоинство TypeScript – это его оснастка. Здесь обеспечивается сложное автозавершение, навигация и рефакторинг. В больших проектах без таких инструментов практически не обойтись. Своими силами вечно не решаешься окончательно изменить код, вся база кода пребывает в сыром виде, и любой крупный рефакторинг становится рискованным и затратным.

Заявление, никак не связанное с реальностью, подаётся в виде безусловной истины. Дальше можно было бы не читать, но я пролистал по диагонали. Собственно, ожидания подтвердились:


Интеллектуальный ввод (intellisense) и простейший рефакторинг (например, переименование символа) коренным образом меняют процессы написания и, особенно, рефакторинга кода. Хотя этот показатель сложно измерить, мне кажется, что рефакторинги, на которые раньше тратилось несколько дней, теперь делаются за несколько часов.

Это прекрасно. Поиск и замену в текстовых редакторах уже запретили отдельным указом, а grep и awk так и вообще предали анафеме. Это теперь рефакторинг, хоть и (снисходительно так) простейший, конечно. Но для этого непременно нужно распоследнее достижение в IDE-строении и 100500 новейших тулзов от Microsoft, иначе никак.


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


Хороший дизайн – это грамотно определенные интерфейсы. А выразить идею интерфейса гораздо проще на языке, который интерфейсы поддерживает.

То, что JavaScript вызывает существенный батхёрт у растекателей мыслью по древу наследования и поклонников строгого исполнения интерфейсных контрактов, это не новость. И попытки сделать из JavaScript что-нибудь похожее на Java или C# тоже не новость.


С TypeScript проще читать и понимать код
Да, я в курсе, что на первый взгляд так не кажется. Тогда рассмотрим пример. Возьмем функцию jQuery.ajax(). Какая информация понятна из ее сигнатуры?

Чтение JavaScript кода очень редко сводится к разбору сигнатур и пониманию параметров. Большая часть времени уходит на попытки понять, что вообще этот код делает в данном контексте. Как в анекдоте: откуда ты это сказал? Чем тут TypeScript со своими плюшками может помочь, ума не приложу.


Почему TypeScript?

Потому что если моск завёрнут вокруг C#, то JavaScript негативен и противен. Вон апологеты Java тоже пытаются до сих пор с GWT и прочими, хотя у них подход чуть другой. Но и то, и другое за пределами своей целевой аудитории обречено натыкаться на недоумённые взгляды: а нафига козе баян? Потому что ES6 требует транспиляции до поры до времени, а вот TypeScript вечно будет обузой.


P.S. Я понимаю, что статья переводная, и стреляю не в пианиста. Ничего личного. :)

Это прекрасно. Поиск и замену в текстовых редакторах уже запретили отдельным указом, а grep и awk так и вообще предали анафеме. Это теперь рефакторинг, хоть и (снисходительно так) простейший, конечно. Но для этого непременно нужно распоследнее достижение в IDE-строении и 100500 новейших тулзов от Microsoft, иначе никак.


Предположим у вас тысяча функций с именем A в тысяче модулей/объектах, которые используются в тысяче мест каждый. Вам нужно найти и поменять имя А на B, но только для одного модуля, включая вызовы функций у переменных, объектов и т.п. Поиск и замена тут не сильно поможет, так как вывести что в одном у том же файле функцию у переменной X надо менять, а у переменной B — не нужно текстовый редактор не сможет, а IDE сможет.

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

А это вообще никак не связано. Тесты и IDE никак не связаны с друг другом. К чем это вообще?

Потому что ES6 требует транспиляции до поры до времени, а вот TypeScript вечно будет обузой.

Как только ES6 перестанет требовать транспиляции, появится новый ES7 на который перейдут все модные фреймворки. Так что не факт.

вывести что в одном у том же файле функцию у переменной X надо менять, а у переменной B — не нужно текстовый редактор не сможет, а IDE сможет.

будьте так любезны проделать рефакторинг этого предложения, а то транспилятор выдает ошибки и смысл не компилируется
Предположим у вас тысяча функций с именем A в тысяче модулей/объектах, которые используются в тысяче мест каждый. Вам нужно найти и поменять имя А на B, но только для одного модуля, включая вызовы функций у переменных, объектов и т.п.

Очередной чисто гипотетический пример, к реальной жизни отношения не имеющий? Если у вас функция foo используется в тысяче мест, то это уже де факто публичное API и переименованию не подлежит. Мы же о серьёзных проектах говорим, да? О тех, в которых обратная совместимость не совсем пустое слово.


Поиск и замена тут не сильно поможет, так как вывести что в одном у том же файле функцию у переменной X надо менять, а у переменной B — не нужно текстовый редактор не сможет, а IDE сможет.

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


Приведу пример реальный, из жизни (моей). Буквально позавчера закончил очередной раунд рефакторинга на 18,000 строк изменений. Общий объём кода в проекте ~350,000 строк + юнит-тесты ещё на 200,000. Раунд это не первый, не второй, даже не десятый, и вполне типичный по объёму.


Так вот: за четыре года таких измывательств над одним из старейших JavaScript фреймворков на рынке мне ни разу не пришлось проделывать массированных переименований fooBar в barFoo. Потому что либо а) это публичное API и оно высечено на скрижалях, либо б) это приватное API и его можно менять, но мне есть чем заняться и кроме таких экзерсисов. А как часто вы такие упражнения проделываете?


А это вообще никак не связано. Тесты и IDE никак не связаны с друг другом. К чем это вообще?

Эхо войны, вселенская битва Бобра с Ослом отражается в разных формах. Есть целая школа мысли, построенная на идее, что баги не надо ловить, их надо предотвращать. Отсюда строгая типизация, отсюда инструментарий, IDE, и прочие штуки. Потому что если в метод нельзя скормить неправильные аргументы, то и проверять, как этот метод работает тоже не надо, так ведь?


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


Ну и как бы, TypeScript? Какой TypeScript? :)


Как только ES6 перестанет требовать транспиляции, появится новый ES7 на который перейдут все модные фреймворки. Так что не факт.

А вы не гоняйтесь за модными фреймворками, вы делом занимайтесь, и будет вам Щастье. Если говорить конкретно о предмете статьи, то про Ангуляр надо чётко осознавать одну простую мысль: это не более чем маркетинговый проект Google, хайп-машина в -цатой степени. Эти мальчики-зайчики уже показали, чего стоит их код, их слова и их "видение"; я ничуть не сомневаюсь, что TypeScript окажется отброшен вместе с другими модными технологиями, как только появится ещё более модное что-нибудь. Просто потому, что парни развлекаются — им эту продукцию продавать не надо, и Гуглу в общем не холодно и не жарко от успеха или провала Angular на рынке.


Что как бы наводит на мысли, нет?

> Очередной чисто гипотетический пример, к реальной жизни отношения не имеющий? Если у вас функция foo используется в тысяче мест, то это уже де факто публичное API и переименованию не подлежит. Мы же о серьёзных проектах говорим, да? О тех, в которых обратная совместимость не совсем пустое слово.

Не скажу за фронтенд, но на яве и тому подобных — может быть множество одноименных функций в разных пакетах (модулях и т.п.). И куча обращений по неквалифицированным именам. В случае со статикой, IDE работает с единицами языка и легко различает эти случаи. Как быть с текстом?
Лучшее, что я слышал от адепта динамических языков и текстовых редакторов — «называйте все методы по разному». Комментарии излишни)
Ой да бросьте вы, человек на полном серьезе говорит, что нужно покрывать тестами типы входных аргументов. Что тут еще сказать можно.
А я не хочу ему доказать. Я хочу, чтобы другие прочли.

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


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


Я вообще уже давно слюной капаю на концепцию fuzzying. Но внедрить такую штуку в масштабах фреймворка будет… эээ… накладно. В смысле, что найдёт столько… :(

А если у вас есть правильный упор на тестирование, то и все эти шаманские пляски с типизацией и инструментарием становятся попросту не нужны.
Разве я не вердно сделал вывод, что тесты вам заменяют проверки типов, например, аргументов функции?
Что проще, дать компилятору/тайпчекеру делать свою работу или проверять, а что будет с функцией, если мы в нее строку запихнем вместо числа?

Возьмем наипростейший пример:
const toFixed = (n: number, precision: number): number => Number(n.toFixed(precision))

Вот что вы тут будете проверять при вызове toFixed('2', 5)? Ловить TypeError при попытке вызвать toFixed на строке? Или завернете в тело кучу проверок на то, что аргументы должны быть числами? Не смешите.
Разве я не вердно сделал вывод, что тесты вам заменяют проверки типов, например, аргументов функции?

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


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


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


Зачем вдруг такое может понадобиться? Да легко: если на вход нашей с вами функции подают содержимое атрибута какого-либо элемента DOM, например. HTMLElement.getAttribute('foo') всегда вернёт строку, которая может пройти через целую цепочку вызовов, а только потом уже попасть в мою функцию. И, скажем, промежуточные вызовы изменить почему-либо сложно. И остаются уже два варианта: либо чуть подправить моё API, чтобы сделать его удобнее к применению в коде, потребляющем это API, или жёстко требовать, чтобы весь вызывающий код приводил типы сам, потому что нам наплевать.


Так вот, исходя из моей практики фашистский подход со строгой типизацией обычно приводит к большему числу проблем, чем более гибкий подход со слабой типизацией. Потому что мне как разработчику API не всё равно, как его будут использовать. Я хочу сделать его проще, удобнее, прозрачнее — вызывайте как вам удобно, а не как мне удобно.


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


Вот что в таких случаях мне сможет предложить TypeScript? Устраивать глубокий рефакторинг с переименованием 100500 функций по всему коду? Спасибо, я лучше быстро подправлю одну функцию, чтобы привести строку к числу, напишу ещё пару тестов для проверки такого ввода, и пойду дальше.

Не то, чтобы такие проверки были совсем бесполезны, но на практике получается, что коллизии типов возникают настолько редко, что тратить время на такие проверки неэффективно.
У нас redux и куча сложных структур в пэйлоадах кучи экшенов. Пытаться все их запомнить и «наугад» тыкать в редьюсерах при разборе просто невозможно. А так мне типизация гарантирует, что к каждому типу экшена привязан свой пэйлоад.
Возникшее исключение будет поймано во время исполнения юнит-теста, и тест закономерно провалится.
Лучшее исключение — несуществующее. Хотелось бы, чтобы тесты падали из-за некорректной логики, а не из-за таких мелочей, как доступ к методу, которого нет. Для этого типизацию, в общем-то, и придумывали.
надо принимать также и строки и приводить их к числу уже внутри функции.
Казалось бы, зачем? Если нужно запихнуть строку, ну так нужно привести ее к числу и только потом уже запихнуть. Почему простейшая функция для работы с числом должна заниматься частными случаями с другими типами? А что будет, если не приведется? Вернуть null, а потом проверять его везде, или выбросить исключение и тоже везде его отлавливать? Лишние проблемы на абсолютно ровном месте. Когда достаточно было просто запретить вызывать со строками. Есть строка — нужно привести, получился NaN — та же проблема, нужно обработать на месте.
которая может пройти через целую цепочку вызовов, а только потом уже попасть в мою функцию
Эта цепочка с тем же успехом может требовать только числа, тогда некорректное значение до конечной функции просто не дойдет, тайпчекер просто не даст.
скажем, промежуточные вызовы изменить почему-либо сложно
Ситуация понятная и известная, хоть и достаточно абстрактная. В любом случае, если уже идут почему-то строки вместо чисел, явно приводить их нужно руками.
вызывающий код приводил типы сам, потому что нам наплевать.
Не потому-что нам наплевать, а потому-что при четко оговоренных начальных условиях и рамках предсказуемость работы функции будет выше, так как она не занимается всякой ерундой вроде приведения типов и обработки ошибочных ситуаций после этого приведения.
Я хочу сделать его проще, удобнее, прозрачнее
В том то и дело, что он становится только сложнее и непредсказуемей, так как появляется целый ворох узких случаев, когда функция ведет себя вот так со строками, вот этак с числами, вот этак с объектами и вообще возвращает, простите, кота, если на вход дать миску с молоком. Документация растет, объем кода растет, объем тестов растет, головная боль крепчает.
Устраивать глубокий рефакторинг с переименованием 100500 функций по всему коду?
Да что вы так прицепились к этому переименовыванию уже :) ну кому-то удобно, кому-то бесполезно. Это небольшой бонус к тому, что реально дает типизация — type safety.
У нас redux и куча сложных структур в пэйлоадах кучи экшенов. Пытаться все их запомнить и «наугад» тыкать в редьюсерах при разборе просто невозможно. А так мне типизация гарантирует, что к каждому типу экшена привязан свой пэйлоад.

Вот это мне ни о чём не говорит вообще, не знаком и комментировать не могу. Завтра если будет время, прогляжу по диагонали этот Redux и отвечу.


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

Исключение это инструмент, а не чёрная метка. Вызов несуществующего метода — это тоже некорректная логика, нет? Не вижу, чем это отличалось бы от других ситуаций.


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

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


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


А что будет, если не приведется? Вернуть null, а потом проверять его везде, или выбросить исключение и тоже везде его отлавливать?

А это уже по обстоятельствам. Про принцип DWIM слышали? А хороший принцип, пригодился бы.


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

Вот как это отличается от выброса исключения и проверки везде в примере выше? А легко: чем больше мест, в которых вы эту функцию вызываете, тем чаще вам надо будет приводить данные вручную. Дупликация кода -> раздувание энтропии -> лишние баги -> жизнь === боль. А можно было всего этого избежать, если не упираться рогом в красную селёдку типизации.


Эта цепочка с тем же успехом может требовать только числа, тогда некорректное значение до конечной функции просто не дойдет, тайпчекер просто не даст.

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


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

Ага, руками всю дорогу. И нарываться на всевозможные подводные грабли с приведением типов? Одно неосторожное движение, и ваш транспилятор TypeScript пропустит что-нибудь типа null + 42, что, конечно же, не вызовет никакой вообще ошибки, а просто даст вам 42. Вместо того, что вы могли бы наивно ожидать.


Вы же не забыли, что исполняться-то вся эта радость будет всё ещё в JavaScript?


И ситуация с "менять нельзя использовать" вовсе не абстрактная, а очень даже реально-каждодневная. Потому что code reuse это наше всё. Умно и с тестами, но всё. А строгая типизация только мешает.


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

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


Вы юниоров не гоняли? Прекрасные перлы в стиле if (Boolean(foo).toString().length === 5) {...} не встречали? А я встречал, много раз. И не только в JavaScript. В C встречал и в C++ встречал, в Java краем глаза заглядывал и плакал, и много ещё где. А уж чего сам во всяких паскалях вытворял юниором, страшно вспомнить.


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


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

Неа, не крепчает, вовсе наоборот. Потому что подавать на вход миску с молоком и ожидать на выходе кота — это малость неразумно. Мы с вами стреляные воробьи, правда? Давайте не кидаться в крайности, а использовать здравый смысл. На практике обычно так и получается: если надо засунуть в функцию миску с молоком и получить кота, то это уже отдельная функция. Которую лучше ещё и назвать по-другому, чтобы понятнее было, а не полагаться на "самодокументирующиеся" перегрузки. К которым у меня отдельный набор лучей ненависти и презрения ещё со времён C++, но это было давно и оффтопик.


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

Как почему прицепился? Да вот поэтому же:


Основное достоинство TypeScript – это его оснастка. Здесь обеспечивается сложное автозавершение, навигация и рефакторинг. В больших проектах без таких инструментов практически не обойтись. Своими силами вечно не решаешься окончательно изменить код, вся база кода пребывает в сыром виде, и любой крупный рефакторинг становится рискованным и затратным.

Мне же автор статьи сказки рассказывает про космические корабли, бороздящие просторы Большого Театра. А я как бы посмеиваюсь. А вы как бы дискуссию поддерживаете.


Это небольшой бонус к тому, что реально дает типизация — type safety.

Type safety, как много в этом слове… Хороший термин, тёплый такой и мягкий, от него на душе становится спокойно и в теле лёгкость всяческая образуется. Отличный маркетинг, жаль только что к реальной жизни отношения мало имеет. :(

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


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


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


Можно всё же конкретный пример из существующего проекта? Я же не просто так спрашиваю, а именно потому, что до сих пор именно на этом моменте все оппоненты как-то уходили в сторону, или отвечали уклончиво. :)

> Если у вас множество одноимённых функций в разных пакетах, то зачем вам переименовывать их все разом?

Речь была о другом. Переименовать функцию в одном пакете, которая используется во многих других пакетах, наряду с одноименными функциями из других пакетах. Как это сделать grep + awk?

> Какая вообще разница, как функция называется, по большому-то счёту? Функция это кусок кода, который что-то делает. Если вы функцию утка() переименуете в лягушка(), она от этого крякать не перестанет и квакать не начнёт.

Ну да, а переменная это кусок памяти. Давайте тогда на ассемблере писать, зачем нам языки высокого уровня?
А серьезный ответ — поддерживаемость.

“Programs must be written for people to read, and only incidentally for machines to execute.”
― Harold Abelson, Structure and Interpretation of Computer Programs
Речь была о другом. Переименовать функцию в одном пакете, которая используется во многих других пакетах, наряду с одноименными функциями из других пакетах. Как это сделать grep + awk?

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


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


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

Вы всерьёз считаете, что публичное API важного модуля можно вот просто так взять и поменять без последствий? Честно, я не знаю, что на такое ответить. Даже язвить расхотелось.


А серьезный ответ — поддерживаемость.

Поддерживаемость практически не связана с названиями функций. Гораздо важнее, что функция делает, нежели как она называется. Энтропия кода неизбежна, любая важная функция в важном модуле со временем обрастает тентаклями, иногда до неузнаваемости. И для поддерживаемости на порядок важнее правильные комментарии в теле кода, которые отвечают на обязательный "WTF?" ещё до того, как он вырвется на свободу.


И таки да, как насчёт конкретного примера из живого проекта?

Я с вами не согласен. Пакет может быть вполне внутренним для проекта. В чем проблема переименовать все вхождения и разом? Для чего? Это называется рефакторинг. Или вы все делаете идеально с первого раза? Или у вас, прости господи, водопад?)

> Энтропия кода неизбежна, любая важная функция в важном модуле со временем обрастает тентаклями, иногда до неузнаваемости.

Я понимаю, обработка corner-cases, которые выяснились в процессе — дело неизбежное.
Но при чем тут нейминг?

> И для поддерживаемости на порядок важнее правильные комментарии в теле кода, которые отвечают на обязательный «WTF?» ещё до того, как он вырвется на свободу.

Ага, а потом код изменили, а комментарии нет. Я считаю, что код первичен.

Пример не дам) Язвите на здоровье)

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


Ещё раз: переименовать все, или не все, вхождения или выхождения, а может быть собаку, а может быть корову, коровы ведь мычат? Не проблема ни в одном языке, JavaScript не исключение. IDE или не IDE, инструментов для этого уже насоздано существенно больше, чем нужно для наступления коммунизма на горизонт.


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


Вы про принцип KISS слышали ведь? Каким боком в него TypeScript вписывается?


Я понимаю, обработка corner-cases, которые выяснились в процессе — дело неизбежное.
Но при чем тут нейминг?

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


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


И менять геморройно, потому что этот модуль уже давно используется ещё чёрти где, см. список выше. Т.е. не то, чтобы вообще совсем нельзя поменять, но сколько времени на это уйдёт, чтобы поменять и протестировать? У нас одна сборка/тест pull request занимает 20 минут в лучшем случае. А у вас?


А тикеты сами собой не закрываются, нет. И скрам-мастер над душой стоит, зараза, дедлайном машет.


Ага, а потом код изменили, а комментарии нет.

А вот за такое надо нежно гладить канделябрами по сусалам в code review. Вы же их делаете, правда?

Вы про принцип KISS слышали ведь? Каким боком в него TypeScript вписывается?

В общем и в целом вписывается. Вернее не именно TypeScript, а указание типов в коде. Возьмём банальную функцию
function add(a, b) {
  return a + b;
}

и ей вариант
function add(a:numeric, b:numeric):numeric {
  return a + b;
}

Неужели первый вариант проще? Только вернуть он может в зависимости от параметров значения двух типов, а принимать вообще все. Вызов add('1', '0') очевидно (для меня) вернёт '10' — это баг или фича функции, что хотел её разработчик?
Вернее не именно TypeScript, а указание типов в коде.

В коде JavaScript подобный синтаксис невозможен, поэтому и рассуждать о нём в отрыве от TypeScript (или подобных ему надстроек) не имеет смысла, согласитесь. А вот что мне удивительно, так это полное игнорирование себестоимости TypeScript в процессе разработки.


Т.е. вы рассуждаете чисто академически, в отрыве от практики: давайте допустим, что… И как бы хлоп, TypeScript уже есть, все разработчики с ним знакомы, сборка настроена, в транспиляторе нет ни одного бага, все нужные библиотеки на него портированы и вообще у нас этакая сферическая модель идеального процесса разработки в вакууме.


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


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


Вызов add('1', '0') очевидно (для меня) вернёт '10' — это баг или фича функции, что хотел её разработчик?

Тут наверное от разработчика зависит. Кто мешает использовать явное приведение типов? Это такие азы языка, которые даже юниорам полагается знать.


function add(a, b) {
    return +a + +b;
}

Но у вас пример опять же очень абстрактный, оторванный от реальной жизни в браузере. Вот ваш же пример, но слегка расширенный:


function getRightSide(left: numeric, width: numeric): numeric {
    return left + width; // Отступ правой стороны в пикселях, всё просто!
}

var element = document.getElementById('foo');

getRightSide(element.style.left, element.style.width); // kaboom :(

Почему кабум? Потому что свойства объекта CSSStyleDeclaration это строки. А ваша функция строки принимать не хочет, хочет только числа. Что будем делать?


function getRightSide(left: string, width: string): numeric { ... }
function getRightSide(left: numeric, width: string): numeric { ... }
function getRightSide(left: string, width: numeric): numeric { ... }

(голосом ослика Иа) И так… далее, и тому… подобное?


Или нет, подождите. Давайте просто приводить типы там, где мы их взяли! Пускай чуть-чуть многословно, ибо таких мест у нас в коде 100500, зато просто и понятно:


var left = parseInt(element.style.left),
    width = parseInt(element.style.width);

getRightSide(left, width); // Voilà!

А через полчаса прибегает наш Главный Дезигнер и даёт нам в глаз с разбегу, потому что мы ему всю раскладку сайта поломали. Э, говорим мы, зачем поломали, куда поломали? А у меня, говорит Главный Дезигнер, на сайте responsive design и все дела. И стили элементов не в абсолютных единицах, а в процентах!


var left = parseInt(element.style.left), // "42%" -> 42
    width = parseInt(element.style.width); // "97.8%" -> 97

getRightSide(42, 97); // Oh shi...

А за Главным Дезигнером в очередь к нашему глазу выстраиваются субподрядчики: они нам кусок сайта ваяют для планшетов, и у них всё по-модному, размеры элементов задаются в em:


var left = parseInt(element.style.left), // "0.2308em" -> 0
    width = parseInt(element.style.width); // "0.9803em" -> 0

getRightSide(0, 0); // Oh sh$t oh sh#t oh *shit*!

А ещё чуть дальше стоит и ехидно прищуривается Младший Дезигнер, он парень молодой, за новинками следит и пытался использовать единицы-фракции размера viewport: vh и vw...


Я продолжать не буду; наверное и так уже понятно, что жизнь наша с вами — пустая жестянка, наполненная лишь болью до краёв (это метафора такая). А ведь можно было оставить нашу функцию как есть и обрабатывать все случаи прямо в ней:


function getRightSide(left, width) {
    var pxLeft, pxWidth;

    if (pixels) {
        pxLeft = left;
        pxWidth = width;
    }
    else if (em) {
        pxLeft = fromEm(left);
        pxWidth = fromEm(width);
    }
    else {
        ...
    }

    return pxLeft + pxWidth;
}

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


Давайте дальше про типы подискутируем? :)

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

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

Я тоже, и потому в настоящем времени написал «использую». А кандидат на внедрение — Flow, поскольку потребует минимального изменения в существующих процессах, да и порог вхождения пониже субъективно для других членов команды.
Кто мешает использовать явное приведение типов?

+a — это не явное приведение типа, а использование нюанса поведения арифметической операции. Хак, проще говоря. Явное в JS выглядит как Number(a).valueOf().
Потому что свойства объекта CSSStyleDeclaration это строки.

Если функция писалась для работы с DOM и т. п., то явный баг описывать параметры числовыми. И этот баг был бы выявлен ещё до первой попытки запуска. Собственно выявление таких багов и есть основная объективная польза от подобных инструментов. Выявили и заменили на, как минимум, или string | number, или просто string, или даже какой-нибудь CSSLength или CSSUnit, а уж внутри парсить и считать как хотим, в том числе возвращая в оригинальных единицах измерения.
В коде JavaScript подобный синтаксис невозможен,
Не соглашусь.

image


У вас какой-то особенный JavaScript? Или мы всё же говорим о расширениях языка?


А кандидат на внедрение — Flow, поскольку потребует минимального изменения в существующих процессах, да и порог вхождения пониже субъективно для других членов команды.

Это до тех пор, пока у вас в команде только основные разработчики, для которых JavaScript и инструментарий вокруг него являются основным предметом внимания каждый день. Теперь представьте, что у вас есть ещё техподдержка человек на 20, которые JavaScript читают со словарём, и QA ещё человек на 10, которые читают неплохо, но писать толком не могут.


Плюс dev ops, технические писатели, продакт-менеджеры и т.п. Чем больше отрыв от ванильной версии языка, тем больше у вас проблем со внедрением надстроек. Вы уверены, что полторы пойманные коллизии типов в год всё это оправдают?


+a — это не явное приведение типа, а использование нюанса поведения арифметической операции. Хак, проще говоря.

Я долго думал, как ответить на это утверждение и в конце концов понял, что в сущности мне нечем крыть. Формально вы совершенно правы, но с практической точки зрения польза от этой правоты примерно как в анекдоте про Холмса и Ватсона: — Эй там, на земле! Подскажите, где мы находимся? — На воздушном шаре!


Я человек практический, и мне платят деньги за то, что я пишу работающий софт, который приносит пользу клиентам и доход владельцам компании. Мне недосуг вникать в детали формальных различий между "явным приведением типа" и "использованием нюанса поведения операции". У меня есть задача: произвести арифметическую операцию над числовым значением, содержащимся в строке. +foo выполняет конверсию строки в число без каких-либо известных мне побочных эффектов, легко читается и, что может оказаться немаловажным, работает быстрее всего в реальных браузерах.


Поэтому "явное приведение типов", которое является явным для меня и не оставляет сомнений при прочтении, будет иметь приоритет перед формально правильными ответами.


Если функция писалась для работы с DOM и т. п., то явный баг описывать параметры числовыми.

Мы всё ещё говорим о JavaScript в браузерах? Там все функции так или иначе работают с DOM. А DOM такая забавная штука, в нём бывают не только строки, но и числа:


<div id="foo" data-foo="bar" style="width:0.5%"></div>

var element = document.getElementById('foo');

var styleWidth = element.style.width; // string
var clientWidth = element.clientWidth; // number

И даже, страшное дело, не только строки и числа:


var dataFoo = element.getAttribute('data-foo'); // string
var dataBar = element.getAttribute('data-bar'); // *null*

И этот баг был бы выявлен ещё до первой попытки запуска. Собственно выявление таких багов и есть основная объективная польза от подобных инструментов.

Объясните мне на пальцах, каким образом статический анализ типов защитит вас от отсутствующего атрибута DOM, попытка взять значение которого вернёт null; или от отсутствующего свойства объекта, попытка взять значение которого вернёт undefined и приведёт арифметику к NaN.


Выявили и заменили на, как минимум, или string | number, или просто string, или даже какой-нибудь CSSLength или CSSUnit,

Или даже на какой-нибудь string | number | null, или ещё на какой-нибудь набор неудобоваримых ключевых слов, который фактически означает "всё подряд и дохлую ворону тоже"?


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

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


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


Честно сказать, все эти попытки прикрутить статическую типизацию к JavaScript напоминают мне анекдот про мужика, который потерянные ключи ищет под фонарём. Потому что там светлее.

У вас какой-то особенный JavaScript? Или мы всё же говорим о расширениях языка?

«Подобный» значит указывающий тип. Jsdoc-комментарии вполне валидный js, с возможностями по указанию типов аналогичный ts/flow. Но хуже читается, как минимум.
Вы уверены, что полторы пойманные коллизии типов в год всё это оправдают?

Гораздо больше, особенно если много слобоквалифицированных писателей кода. И читать легче.
Поэтому «явное приведение типов», которое является явным для меня и не оставляет сомнений при прочтении, будет иметь приоритет перед формально правильными ответами.

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

что может оказаться немаловажным, работает быстрее всего в реальных браузерах.

Преждевременная оптимизация зло и любая оптимизация с использованием неочевидных особенностей языка должна документироваться.
Мы всё ещё говорим о JavaScript в браузерах? Там все функции так или иначе работают с DOM.

Не все. DOM может использоваться только загрузки и запуска функции (даже без аргументов из DOM), а сама функция, например, осуществляет работу с сервером. Вот сейчас в бэклоге задача «прокси-сервера» — скрипт открывает веб-сокет соединение с одним сервером и посредством XHR транслирует запросы от него на другой.
А DOM такая забавная штука, в нём бывают не только строки, но и числа:

У меня нет желания расписывать каждый нюанс. Суть в том, что редко бывают функций «вообще», способные принимать и корректно обрабатывать любые типы значений, обычно разработка функции предполагает некий контекст из которого она будет вызываться. И статическая типизация позволяет разработчику функции четко указать пользователям функции для какого контекста она предназначена
Объясните мне на пальцах, каким образом статический анализ типов защитит вас от отсутствующего атрибута DOM, попытка взять значение которого вернёт null; или от отсутствующего свойства объекта, попытка взять значение которого вернёт undefined и приведёт арифметику к NaN.

Не пропустит код, в котором значение после попытки его взять не проходит явную проверку на null/undefined, если источник значения не помечен как возвращающий значения строго определенного типа(ов), а его использование конкретный тип(ы) предполагает.
который фактически означает «всё подряд и дохлую ворону тоже»?

Есть any или синонимичное ему неуказание типа в принципе (вроде в ts нельзя).
Просто потому, что потенциальные проблемы, могущие возникнуть на этапе компиляции и потенциально устраняемые статической типизацией, возникают на порядки реже реальных проблем, возникающих на этапе исполнения и устраняемых только и исключительно тестированием.

Одно другого не исключает. Статическая типизация не серебряная пуля, а лишь одно из средств повышения качества кода, уменьшающая как количество необходимых тестов, так и проверок в рантайме, равно как и приведения в нём типов. Написав какое-нибудь add(a: number, b: number): number для функции которую я пишу для сложений чисел, мне не нужно ни тесты писать для строк и объектов, ни использовать явные или неявные приведения типов, ни проверки на передачу null, undefined или объектов.
«Подобный» значит указывающий тип. Jsdoc-комментарии вполне валидный js, с возможностями по указанию типов аналогичный ts/flow. Но хуже читается, как минимум.

Если честно, я уже потерял нить дискуссии и не очень понимаю, о чём идёт речь. Комментарии к коду JavaScript являются валидным синтаксисом и вопросов к ним нет, будут работать в любом браузере. Никаких инструментов для преобразования кода при этом не нужно, но и никаких возможностей по принудительной проверке типов комментарии тоже не дают. Это просто документация. Документирование типов не вызывает у меня никаких возражений: раз уж они (типы) есть и их приходится использовать, то можно и нужно документировать.


О чём мы здесь дискутируем?


Гораздо больше, особенно если много слобоквалифицированных писателей кода. И читать легче.

Мой совет может показаться циничным, но, опять же исходя из моего опыта: не нанимайте низкоквалифицированных писателей кода, это плохой подход. Используйте code review, парное программирование, назначьте merge master — есть масса способов решить эту управленческую проблему без привлечения сомнительных технических подходов.


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

Я не вижу здесь противоречий. Основные разработчики, о которых идёт речь, занимаются разработкой проекта. Остальные — техподдержка, QA и прочие — могут использовать проект, читать код, тестировать клиентские проблемы и т.п., но коммит-доступа у них нет. Соответственно нет и проблем с неприлично кривым кодом в основном проекте.


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

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


Вот сейчас в бэклоге задача «прокси-сервера» — скрипт открывает веб-сокет соединение с одним сервером и посредством XHR транслирует запросы от него на другой.

Пример интересный, но я бы не стал делать такие вещи в браузере по множеству причин. А на серверной стороне у меня есть выбор, и JavaScript пойдёт лесом без разговоров.


Суть в том, что редко бывают функций «вообще», способные принимать и корректно обрабатывать любые типы значений, обычно разработка функции предполагает некий контекст из которого она будет вызываться.

С этим утверждением я совершенно согласен.


И статическая типизация позволяет разработчику функции четко указать пользователям функции для какого контекста она предназначена

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


Не пропустит код, в котором значение после попытки его взять не проходит явную проверку на null/undefined, если источник значения не помечен как возвращающий значения строго определенного типа(ов), а его использование конкретный тип(ы) предполагает.

Замечательно. Т.е. фактически ваш type checker не даст мне вызвать DOM API напрямую, а вместо этого мне придётся написать кучу обёрток под разные типы возвращаемых данных только ради того, чтобы удовлетворить требованиям строгой типизации? Где там эта бритва...


Есть any или синонимичное ему неуказание типа в принципе (вроде в ts нельзя).

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


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

Отлично! По крайней мере мы продвигаемся вперёд. Тестировать код необходимо, проверки кода на этапе компиляции от всех проблем не спасают. Согласны?


мне не нужно ни тесты писать для строк и объектов, ни использовать явные или неявные приведения типов, ни проверки на передачу null, undefined или объектов.

Так я как раз об этом и говорил в предыдущем сообщении: что писать тесты для строк и объектов, приведения типов и всего остального просто не нужно. Коллизии типов, если они и возникнут, приведут к сбоям основных тестов и будут выловлены без дополнительных усилий. Я никак не могу взять в толк, почему эта простая мысль воспринимается с таким трудом.


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

Если честно, я уже потерял нить дискуссии и не очень понимаю, о чём идёт речь.

Две темы смешалось. Нужна ли вообще статическая типизация в JS и конкретные способы её реализации.
но и никаких возможностей по принудительной проверке типов комментарии тоже не дают.

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

Использование неочевидных хаков для оптимизации должно документироваться. Иначе их кто-то может либо вообще удалить, либо привести к очевидному виду. А выявиться проблема может очень не скоро, например, если вы написали +a лишь с прицелом на будущее, и на момент написания в коде это не использовалось, всегда числа передавали.
Т.е. фактически ваш type checker не даст мне вызвать DOM API напрямую, а вместо этого мне придётся написать кучу обёрток под разные типы возвращаемых данных только ради того, чтобы удовлетворить требованиям строгой типизации?

Подобные вызовы DOM API помечены как any «из коробки», пишутся обертки над ними, если принято решение в проекте, что это нужно делать для улучшения надежности, чтобы исключить в таких случаях ошибки типа разыменования свойства на null или undefined
Так я как раз об этом и говорил в предыдущем сообщении: что писать тесты для строк и объектов, приведения типов и всего остального просто не нужно. Коллизии типов, если они и возникнут, приведут к сбоям основных тестов и будут выловлены без дополнительных усилий. Я никак не могу взять в толк, почему эта простая мысль воспринимается с таким трудом.

Я не приму код типа f = (a, b) => return +a + +b; если не предполагается его использовать с отличными от number параметрами, и без покрытия тестами для предполагаемых случаев, если предполагается.
И всё же, как насчёт конкретного примера проблемы в реальном проекте, наткнувшись на которую вы поняли, что вот всё, без строгой типизации дальше никак?

Примеров тьма, какой-то конкретный случай может быть лишь последней каплей. Ну вот сегодня я потратил 5 минут на то, чтобы обнаружить, что получил в параметре массив, а не объект как ожидал и задокументировал. Причём получал его давно, но обращался к свойствам через переменную obj[key], а тут понадобилось напрямую obj.key. Сегодня пять минут, в другой раз минуту, когда-то полчаса — не угадаешь какой случай станет последней каплей.
Две темы смешалось. Нужна ли вообще статическая типизация в JS и конкретные способы её реализации.

Просто замечание: насчет Флоу не знаю, но тайпскрипт — это не "статическая типизация".

Две темы смешалось. Нужна ли вообще статическая типизация в JS и конкретные способы её реализации.

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


Единственное значимое отличие — у Flow этап вырезания обязательный, а сам этап проверки так же как и у jsdoc — нет.

Теперь понятно, откуда конфуз: я даже не знал, что JSDoc такие штуки умеет. Ну, пожать плечами только.


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

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


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


А выявиться проблема может очень не скоро, например, если вы написали +a лишь с прицелом на будущее, и на момент написания в коде это не использовалось, всегда числа передавали.

Зачем мне писать какой-то мертворожденный код с прицелом на будущее? Это как в анекдоте про эстонского фермера и дохлую ворону, которая "Приккотиттся?" Ну, я уже довольно давно работаю в этой индустрии и много раз убеждался, что в 90% случаев такой код "Не приккоттиллся."


Я уже говорил, что у нас есть большая батарея тестов, позволяющая делать рефакторинг легко и непринуждённо. Если мне понадобится принимать строки в функции, которая раньше принимала только числа, я добавлю минимальные изменения, допишу тесты и отправлю pull request на полный прогон в CI, а сам пойду заниматься следующим тикетом.


Через 20 минут проверю результаты, если ничего не сломалось — замечательно, PR уже висит в очереди и я не потерял времени. Если сломалось, я сделаю пометку, что merge делать не надо, и вернусь к этой ветке после того, как закончу работать с текущей на данный момент. В любом случае регрессия незамеченной не останется.


Подобные вызовы DOM API помечены как any «из коробки»

Т.е. фактически ничем не способствуют святому делу статической типизации?


пишутся обертки над ними, если принято решение в проекте, что это нужно делать для улучшения надежности, чтобы исключить в таких случаях ошибки типа разыменования свойства на null или undefined

А разве не это является конечной целью? И если этого не делать, то в чём тогда вообще смысл TypeScript/Flow/whatever?


Я не приму код типа f = (a, b) => return +a + +b; если не предполагается его использовать с отличными от number параметрами, и без покрытия тестами для предполагаемых случаев, если предполагается.

Ну, не принимайте, ваше право. Я не в курсе специфики вашего проекта, может быть такой подход и оправдан. У нас же такие строгости будут чрезвычайно контрпродуктивны, т.к. основное время и силы тратятся на другие задачи: бодание с браузерами, обход багов в flexbox, приручение CSS, обеспечение совместимости с предыдущими версиями фреймворка, проектирование API для новых фич, поиск и устранение багов в старом коде, и т.д., и т.п.


Бывают задачи и весьма эзотерические, например мне пришлось один раз потратить полгода на то, чтобы разобраться в нюансах работы accessibility API в браузерах, экранных читалок (screen readers) и их связке, чтобы "сделать фреймворк доступным". Это задача так стояла. Решение вылилось в масштабный рефакторинг всего фреймворка, который занял два года и зацепил примерно 80% базы кода.


Вы уж извините, но мне правда некогда сдувать пылинки с JavaScript. Работа стоит.


Примеров тьма, какой-то конкретный случай может быть лишь последней каплей. Ну вот сегодня я потратил 5 минут на то, чтобы обнаружить, что получил в параметре массив, а не объект как ожидал и задокументировал. Причём получал его давно, но обращался к свойствам через переменную obj[key], а тут понадобилось напрямую obj.key.

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


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

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


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


При этом сама концепция статической типизации как панацеи для устранения проблем с коллизиями типов воспринимается как аксиома, безотносительно специфики JavaScript. Которая, если присмотреться поглубже, делает эти расширения если не совсем уж бессмысленными, то как минимум существенно менее пуленепробиваемыми, чем в теории. Какой толк от статической типизации в Angular, если исполняться будет сборка angular.js + ещё 100500 разных JavaScript библиотек, которые о статической типизации ни слухом ни духом? Что дадут проверки TypeScript, если компиляция была давным-давно в далёкой-далёкой галактике, а в рантайме вызов функции d3.js вернёт SVGElement вместо HTMLElement, который вы ждёте?


Надо также принять во внимание, что до сих пор я не видел ни одного решающего аргумента в пользу статической типизации, как в рамках данной дискуссии относительно JavaScript, так и за её пределами. Основные аргументы пропонентов таковой сводятся либо к аппелированию к авторитетам ("все серьёзные проекты написаны на серьёзных языках!"), либо к тактике устрашения ("а вот если бы не было типизации, то небо обрушилось бы!"). Однако даже самые ярые сторонники статически типизированных языков вынуждены признать, что все проблемы этот подход не решает, и свидетельством тому обилие фреймворков для юнит-тестирования в Java, C#, etc.


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


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


Моя позиция далека от непримиримой, но мне не нравится, когда чьё-то мнение выдаётся за истину, не подлежащую сомнению. Если вы хотите перевернуть мой мир, то для этого нужна серьёзная, устойчивая опора. :) Я таковой пока не вижу и подозреваю, что переворачивание моего мира в ваши ближайшие планы не входит; пофлеймить же всласть я уже успел и тема несколько обрыдла. Поэтому предлагаю её закрыть, процитировав любимую фразу моего босса: Let's agree to disagree. ;)

Какой толк от статической типизации в Angular, если исполняться будет сборка angular.js + ещё 100500 разных JavaScript библиотек, которые о статической типизации ни слухом ни духом?

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

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

Я и сами их могу привести, поскольку редко пишу на статических от рождения языках последние лет 20. Но попытки привнесения опциональной статической типизации в языки типа PHP и JS только приветствую как объединение лучшего из двух миров. Да, нужно знать меру и у всего хорошего есть своя цена.
Поэтому предлагаю её закрыть

Пожалуй, пора, да.

Не сарказма ради, а просвещения для поинтересуюсь: а как часто вам нужно проделывать такие массированные переименования переменных/функций/чего там, чтобы именно эта возможность стала чуть ли не ключевой при выборе инструментария?

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

Так вот: за четыре года таких измывательств над одним из старейших JavaScript фреймворков на рынке мне ни разу не пришлось проделывать массированных переименований fooBar в barFoo. Потому что либо а) это публичное API и оно высечено на скрижалях, либо б) это приватное API и его можно менять, но мне есть чем заняться и кроме таких экзерсисов. А как часто вы такие упражнения проделываете?

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

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

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

У меня растёт и крепнет ощущение, что мы всё же разговариваем о совершенно разных масштабах мух и котлет. Приведение разных исходников в одному виду? Зачем? Вам серьёзно больше заняться нечем? У вас все тикеты закрыты, все фичи пофичены и все баги пофиксены? Все тесты написаны и проходят во всех браузерах каждый раз? Автоматизированная проверка тестового покрытия даёт 100% зелёный результат? Пользователи не жалуются на производительность? Бизнес не прискакивает на белом коне с новыми нереальными запросами каждые две недели?


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


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

Конечно слишком сложно, но не из-за отсутствия инструментов и IDE. Этого добра для любых языков уже наваяли по самое не балуйся. Код менять нельзя без необходимости, потому что это увеличивает энтропию, всегда. Любое изменение, даже самое маленькое, открывает щёлочку для усатых друзей. При достижении определённых размеров любой проект становится слишком завязанным на разные углы кода, чтобы можно было безболезненно его менять, и к любым изменениям приходится подходить очень осторожно.


Радуйтесь, что ваш проект этих размеров ещё не дорос и житие ваше легко и безоблачно.


К вам приходит аналитик и говорит что он ошибся и переменная revenue (доход), которая используется в ваших бизнес объектах это на самом деле taxValue (сумма уплаченных налогов). Оставить в вашем приватное API название вводящее в заблуждение очевидно плохая идея, самодокументирующийся код придумали не просто так, чем проще можно будет поменять один код на другой тем лучше.

Конечно, меняйте. Это ведь приватное API модуля и изменения только внутри, правда? Оно никак не должно отразиться на поведении публичного API и других модулей, тем более что всё публичное API у нас с вами закрыто юнит-тестами вдоль и поперёк на 100%, см. выше. И заняться нам с вами больше нечем, мы уже выяснили.


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

Грх… мм… ыы… кх-кх… Пардон, это я кофем подавился. От острого приступа восхищения. Вот просто так, просто взять и по-быстрому заменить одну библиотеку на другую? И функции тоже поменять? И все побочные эффекты нам TypeScript учтёт, и тесты перепишет, и браузерные косяки уравняет, и от шестиствольного BFG-9000 в ногу спасёт надеванием армированных лосин с гульфиком из анобтаниума?


Wow. Much cool. Such wonderful. Wow.

Вот вроде в соседней ветке даже диалог вышел, а тут вы вот такое устраиваете.

В соседней ветке диалог вышел, потому что вы аргументы приводите и дискутируете. А тут мне опять сказки пытаются рассказывать, только уже не про Большой Театр, а про Малый.


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


Впрочем, обидеть тоже не хотел, ни вас, ни terryP. Приношу извинения.

Радуйтесь, что ваш проект этих размеров ещё не дорос и житие ваше легко и безоблачно.

Ооо, я работал на проектах в которых кол-во строк сопоставимо с последней версией Windows или Linux. Да, есть и такие бизнес решения. Боюсь это ВЫ просто не понимает что такое по-настоящему большой проект.

Код менять нельзя без необходимости, потому что это увеличивает энтропию, всегда. Любое изменение, даже самое маленькое, открывает щёлочку для усатых друзей.

Это вы так похоронили рефакторинг как класс?

При достижении определённых размеров любой проект

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

Приведение разных исходников в одному виду? Зачем? Вам серьёзно больше заняться нечем?

Вы сейчас наглядно демонстрируйте стиль под названием быдлокодерство. Архитектура, рефакторинги, стили, качество кода — нафиг, надо лабать. Бизнес требует. Понимаете, в бекэнде давно уже поняли что до определенного предела костыли и велосипеды работают, а потом код начинает пахнуть и разваливаться. Во фронтенде часто именно такой стиль «Времени нет, надо фигачить», просто потому что проекты пока относительно небольшие (реально большие вроде поисковой машины гугла или исходного кода Oracle DB, в браузер пока не засунуть). Печально, что при этом учить пытаются.

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

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

И заняться нам с вами больше нечем, мы уже выяснили.

Во-первых, сейчас диалог напоминает разговор с дикарем, когда он пытается доказать что барабан отличное средство связи куда лучшее чем телефон. Вот больше чем уверен, что со нормальными статическими языками (Java, C++, C#) вы никогда серьезно не работали и даже не представляете почему их предпочитают в реально больших проектах.

Во-вторых, про реально большие проекты и статическую типизацию, вы сейчас напоминаете Д'артаньяна и п**ов, вот всякие Гулы, Линуксы и Микрософты придумали языки со статической типизацией для своих реально больших проектах, но вот вы Д'артаньян, а все они п**сы вы знаете решение «Пишите больше тестов! Тесты вас спасут!». Ага, в миллионы умных людей до этого не догадались, на придумывали какую-то статическую типизацию.

То что тесты нифига не спасают от того что программист не учел (знаете что такое SQL injections и почему в языках с динамической типизацией его провести легче?), а 100% автоматическое покрытие это вообще профанация, так как заставляет тестировать не реально нужное, а даже тот код где тесты и не нужны (а он есть всегда).
Ооо, я работал на проектах в которых кол-во строк сопоставимо с последней версией Windows или Linux.

Алярм! Алярм! Всем постам! Боевой космический флот Большого Театра вышел на орбиту Альфы-Центавры! Срочно занять позиции на форменном диване и надеть уставные кепки с тремя козырьками!


Не, ну мне лениво эту картинку с шоколадной фабрикой сюда запихивать, так что вы просто представьте. :)


Это вы так похоронили рефакторинг как класс?

Неа. Если вы почитаете выше, то именно как раз об этом я и говорил: что рефакторинг делать надо, и часто, и делаем. Потому что и старого говнокода полно, и новый добавляется, и вообще энтропию не победить. Можно только пытаться сдержать.


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


И кстати, кроме собственно проверки кода этот набор тестов позволяет делать ещё много чего такого, что обычным QA тестерам и не снилось. Если интересно, могу рассказать. :)


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

А кто-то собирался с этим спорить? :)


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

Уважаемый Дон Кихот, я готов внимать вам внематочно ещё долго, жгите на здоровье. Только с коня-то слезайте, он надувной. Лопнет ещё от накала страстей. :)


Во фронтенде часто именно такой стиль «Времени нет, надо фигачить», просто потому что проекты пока относительно небольшие (реально большие вроде поисковой машины гугла или исходного кода Oracle DB, в браузер пока не засунуть). Печально, что при этом учить пытаются.

А вы не печальтесь, поучиться всегда есть чему. Можно начать с азов, например с навыков внимательного чтения того, что написано, а не воображаемых междустрочий. :)


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

Вот! Вот оно, откровение, которого мне не хватало столько долгих тёмных лет! Распечатаю и повешу на стену в Ленинском уголке, прямо вот рядом с иконой Кернигана-и-Ритчи.


Хотя нет, сперва всё же подожду конкретных примеров из проектов на миллионы у.с. (условных строк), которые налабали правильные поцоны "на бекенде", походя меняя архитектуру направо и налево в зависимости от фазы луны, модных тенденций и у моря погоды. :)


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

Ага, аргументы кончились, начался переход на личности, брызганье слюной и оскорбления. Ветку можно закрывать? :)


То что тесты нифига не спасают от того что программист не учел (знаете что такое SQL injections и почему в языках с динамической типизацией его провести легче?)

Даа, пожалуй и точно можно закрывать. Бармен, Сириусу больше не наливайте!

Typescript не убирает те возможности, которые даёт JS, а расширяет их. В некоторых случаях удобен интерфейс и статическая типизация, в другом динамическая. В одой команде большая часть людей работали с фронтендом многие годы, в другом куча людей с бекенда, которые JS знают, но без знакомых абстракций теряются. В одном проекте 20k строк, в другом 200k, в третьем 1M+.
И то, и другое не более, чем инструменты. А с точки зрения машины, удобнее всего минифицированный JS, поэтому его всё равно лучше процессить.

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

Начну с середины:


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

Парсеру JavaScript абсолютно всё равно, минифицированный у вас JavaScript или нет, семантически незначительный текст и разметка всё равно выкусывается первым шагом. Виртуальной машине в движке JavaScript ещё более всё равно, она работает с абстрактными объектами, отвязанными от синтаксиса уже совсем. Браузеру вообще на всё наплевать, он выставляет в сторону движка афедронDOM API и ему даже пузико не щекочет.


Остаётся кто? Да мы с вами, болезные. Только нам не всё равно, какой код был на входе и какой получился на выходе. Потому что нам его дебажить. Чем больше прослоек между исходной абстракцией и реальным интерпретируемым кодом, тем более глубокая нам выкопана яма.


Не верите? А вы подождите, пока у вас вылезет первый тикет из серии "аборт по телефону". Я-то свой JavaScript и в минифицированном виде прочитаю, если приспичит, а вот вам с TypeScript тяжковато будет.


Далее везде:


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

Вы, собственно, подтверждаете мои слова: целевая аудитория TypeScript это огромная когорта C# программистов, которым легче принять что-то промежуточное между JavaScript и C#, чем чистый JavaScript сам по себе.


Что некоторым образом противоречит вашей следующей замечательной мысли, с которой я целиком согласен в отрыве от данного контекста:


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

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


Что, впрочем, не означает, что я не симпатизирую данной вселенской пичальке в принципе. JavaScript это ужасный язык, но он есть реальность, данная нам в браузерах. Другого не дано. Пытаться делать из него C#, Java, <вставьте по вкусу> — это если не Прямой Путь в Адъ, то как минимум контрпродуктивное заблуждение.


Эту бы энергию, да на мирные цели...

Вы, собственно, подтверждаете мои слова: целевая аудитория TypeScript это огромная когорта C# программистов, которым легче принять что-то промежуточное между JavaScript и C#, чем чистый JavaScript сам по себе.

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

Ага, то-то джаваскрипт обзавелся извращением под названием jsdoc, который, помимо комментирования, еще и пытается добавлять информацию о типах.

И? В JavaScript есть типы значений; их приходится использовать, и это не отменишь, к сожалению. Ограничение языка. Какой смысл не документировать типы принимаемых значений, раз уж они всё равно есть?


К тому же, не нравится — не используйте. Это вам не JavaDoc с аннотациями, ага.

Какой смысл не документировать типы принимаемых значений, раз уж они всё равно есть?
Вы же против типизации, принимаемым значениям нужно не типы проставлять, а покрывать их тестами!

Нет, ну вы мне честно скажите: вы правда не понимаете разницу между документированием принимаемых типов аргументов и принуждением к использованию только определённого набора типов? Или вы просто придуриваетесь? В первом случае объясню, во втором попридуриваюсь за компанию. :)

Мы все еще про тайпскрипт говорим? Там тоже типизация аннотация типами опциональная. О каком принуждении идет речь?


Подозреваю, что вы тайпскрипт не знаете, а думаете, что это нечто типа Java, только компилируется в джаваскрипт.

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


Я никогда не утверждал, что знаю TypeScript. С чего вы взяли, что он мне настолько интересен? У этого языка очень чёткая целевая аудитория и не менее чёткая основная цель: обеспечить целевой аудитории иллюзию контроля над ситуацией, чтобы у несчастных адептов ООП и статической типизации башню не сносило от "дикого запада" JavaScript, где не уважаются чоткие принципы поцонов с бекенда.


А за пределами этой самой целевой аудитории все важные рассуждения на тему острой необходимости TypeScript и надувания щёк воспринимаются недоумённо и делают немножечко смешно.


И кстати да, если аннотация типами опциональная, то какой вообще смысл в TypeScript? Просветите пожалуйста.

Презентация устарела. В ts появились нул-чеки (адски раздражают, но при этом не менее полезные), которым тут посвящено несколько слайдов. Проверка по содержимому в typescript сделана намеренно. Так или иначе он должен быть совместимым с javascript, а в нем наплодили столько библиотек с кривыми реализациями, что порой проверка по содержимому спасает. Особенно при миграции на этот язык.


В отличие от flow, который в случае, если импортишь какой-то объект, записанный как обычный js-object, становится бессильным. И так далее.


Как раз таки flow мертворожденный и его нужно закапывать, ведь:


а: появился он как костыль от фейсбука, потому что они ж фейсбук, не повадно пользоваться наработками других (к слову, они сами же вместо своего flow используют typescript в том же immutable.js, remodel. React на flow, но тесты в нем на ts (https://github.com/facebook/react/tree/master/scripts/jest), ну и всячески пытается быть похожим на typescript,


б и гораздо важнее: typescript, в отличие от flow, не только абстракция для проверки типов, а целый отдельный язык, который заставляет писать более читаемый код, думать над интерфейсами и что куда будет передано, а не из серии "ну засуну это в функцию, а она уже внутри сама разберется". Он заставляет выстраивать адекватные архитектуры и всячески мешает создать типичную для js лапшу.


Ну и с: он добавляет кучу полезных вещей, таких как private, protected и public, enum, generic и прочее. Конечно, не все работает так, как хочется (я, например, хочу template вместо generic), но лучше чем ничего.

Презентация устарела. В ts появились нул-чеки (адски раздражают, но при этом не менее полезные), которым тут посвящено несколько слайдов.

«Презентацию не смотрел, но осуждаю» (с). Вообще-то там разговор идет про ts2 и про включение нулл-чеков. Существует первая версия этой презентации, но я привел ссылку на ts2.
Проверка по содержимому в typescript сделана намеренно.

Неужели? Городить все проверки типов, в любом случае требовать транспиляции в js и говорить, что так и задумано, что есть 2 разных типа с одинаковым набором свойств и я могу скастовать из одного в другой, а потом еще хуже — начать обрабатывать инстанс первого класса как инстанс второго. Пример со слайдов показателен. Если так можно делать, то зачем тогда вообще все это?
Так или иначе он должен быть совместимым с javascript

Он изначально несовместим с js если это прямой ts с типами, ради проверки которых он и задумывался, а не просто переименованный файлик с js кодом, потому что это расширение js, а не наоборот. Так как нужна транспиляция — это уже не валидный js и можно надстраивать его как-угодно.
React на flow, но тесты в нем на ts

И где в нем тесты на ts? Описание модели для интеллисенса в ide — да, но не более.
всячески пытается быть похожим на typescript

Ну так синтаксис flow был похож на ts, оно стало разрабатываться после неудачного опыта работы фейсбучников в большом проекте с ts.
он добавляет кучу полезных вещей, таких как private, protected и public,

То есть тут можно добавлять полностью несовместимые с js понятия, а при сравнении типов по структуре — нет? Двойные стандарты. :)

Посмотрел я на презентацию.


1  let cats: Array<Cat> = []; // can only contain cats
2  let animals: Array<Animal> = []; // can only contain animals

// error TS2322: Type 'Animal[]' is not assignable to type 'Cat[]'.
4  //  Type 'Animal' is not assignable to type 'Cat'.
5  //    Property 'purrFactor' is missing in type 'Animal'.
6  cats = animals;

7  // wow, works, but is no longer safe
8  animals = cats;

Я так понимаю, что Cat наследует Animals. Ну и чего тут некорректного? Кто-то плохо учился и не слышал о полиморфизме? К слову, я бы в этом примере говнился на Flow, что он не умеет в полиморфизм.


То есть тут можно добавлять полностью несовместимые с js понятия

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


Фейсбук тыкал тайпскрипт когда тот еще был совсем молодой. Они написали свою альтернативу, которая, безусловно, повлияла на дальнейшее развитие самого тайпскрипта. Только сейчас тайпскрипт догнал и обогнал флоу, превратив последнее в уж совсем сомнительный инструмент. Начиная со второй версии, когда тайпинги стали тянуться адекватно и самостоятельно (без комментария /// refference) — каких-то трудностей в его использовании нет. И да, он, как и flow, самостоятельно выводит типы, поэтому даже от совсем тупого переименования js в ts уже будет, хоть и крошечная, но польза.


Вдобавок, тайпскрипт, в отличие от coffeescript и прочих поделок над js, заметно всколыхнул общественность, что привело к появлению стандартизированных тайпингов для кучи библиотек, и если IDE по-умнее, она в состоянии их самостоятельно вытянуть и помогать разрабатывать даже если вы пользуетесь совсем обычным js. И с typescript приехал language server, который, на мой взгляд, вообще может стать революцией в мире IDE и редакторов кода.


Основная причина, почему есть flow, и многие его предпочитаю вместо typescript — предрассудки. Если сравнивать два этих языка (flow, по сути, язык, покуда просто засунуть в браузер этот же код не получится) — на данный момент у Flow практически нет преимуществ, ну разве что с issue в тайпскрипте действительн бывают затыки. Однако многие возводят в боги facebook, а то, что вышло из недр microsoft, по их мнению, априори не должно жить, поэтому и находят какие-то оправдания, почему последний — зло и ерунда, а flow — серьезно и на века.


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


P.S. Говорил, что у typescript есть проблемы с резолвом issue. Посмотрел статистику — у ts 1800 открытых к 8000 закрытых issue, у flow — 1000 открытых к 1300 закрытых. Нда, facebook наезжает на ms за то, что те не резолвять issue, а сами то.

flow, по сути, язык, покуда просто засунуть в браузер этот же код не получится

Да ладно, это как раз не язык, а чистый js с типами в коде вместо jsdoc-комментов. ему не нужен сложный транспайлер типа tsc, типы отрезаются простым плагином под babel — это по сути все, что нужно сделать. Я на самом деле не являюсь пользователем ни того, ни того, предпочитая чистый js + jsdoc на сервере — не нравится мне сама идея промежуточной стадии транспиляции + необходимость настройки sourcemaps для дебага, проблемы с интеграцией в разные IDE и т.п. Например, tsc под vscode неадекватно работает в watch mode если запускать изнутри — приходится запускать снаружи из консоли, а flow работает только как постпроцессор, ну и относительно недавно вышел под windows (мне не критично, но люди мало слышали, что не typescript единым все ограничивается).
Да ладно, это как раз не язык, а чистый js с типами в коде вместо jsdoc-комментов.

Вот эту фразу я никогда не понимал. Засунуть код, написанный на flow в браузер — заработает? Нет. Нужно компилировать? (препроцессить). Нужно. С чего он вдруг не язык? Таким образом, C — препроцессор ассемблера — там всего то человекочитаемые конструкции преобразуются в ассемблерный код. И ассемблер — лишь препроцессор машинного кода, там вообще просто MOV переписывается на что-то типа 0b011010100101010.


И, раз на то пошло, тайпскрипт не сильно сложнее — в режим es6 он тоже лишь убирает типы и модификаторы области видимости, может работать в виде плагина к бейбелю.


tsc под vscode неадекватно работает в watch mode
Тут уже вебпак вот второй недавно вышел, меня вообще немного корежит от компиляции чего-либо в текстовом редакторе (VS Code), при этом дебаг работает великолепно и в связке webpack-chrome-ts-vsCode.

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

Нужно компилировать?

Не нужно, в отличие от ts — flow не умеет в es5 / es6 и тп — он тупо чекает типы. Отпроцессить поток на невалидные ключевые слова — да, как и ts. Но в данном случае tsc — это больше, чем выкусывалка невалидных в js токенов, оно требует указания — во что оно будет транспайлить.

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

Однако он генерит код для импорта модулей, в тот же commonjs, например. flow — это не про генерацию кода.

А при target: es6 он импорты оставляет как есть. Да, он генерирует код при модификаторе private, но в выборе "какая-то одна из стадий pipeline в фоне мне сгенерирует немножко кода" и "у меня вообще не будет модификатора private" я предпочту первое. Я все равно не понимаю, зачем выбирать меньший функционал, который не обладает преимуществами? По поводу полиморфизма с генериками — сильно не уверен, бага это или так и должно быть. С одной стороны — логично, что массив более базового класса может принимать в себя элементы своих потомков, а если пишешь подобный код — сам стреляешь себе в ногу, с другой — строгая типизация действительно не должна позволять мне засунуть в массив объект, который находится выше или вообще в другой ветви иерархии наследования.

Так понятно, что будут допиливаться как то, так и другое. Но хотелось бы максимально строгой проверки типов, как в c# / java, для упрощенки есть jsdoc и валидаторы чистого js кода по ним.

Кому хотелось бы? Лично вам?
Пишу на C# каждый день, и при этом считаю что для фронта C# с его системой типов не подходит абсолютно. Нужна система типов, но более гибкая в определенных моментах как, например, в ts.


Вообще все обсуждение началось с вопроса "почему не на flow?", ну тогда и я спрошу: а почему надо не на ts а на flow? Пока что во всей этой ветке не прозвучало ни 1 нормального аргумента в пользу flow. Все аргументы в стиле "ну там структурная типизация и это плохо, потому что потому."

Я все равно не понимаю, зачем выбирать меньший функционал, который не обладает преимуществами?

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

Typescript сложнее настроить. С target ES5 он генерирует кучу не нужного для современных платформ кода, с target ES6 генерирует код, который потребует компиляции в ES6 без import/export, и вообще не факт (могу заблуждаться), что сможет обработать типизированный JS-код stage-0, а не свалится с синтаксическими ошибками.
препроцессор JS-кода, а не компилятор с одного языка на другой. Он не генерирует JS-код, а лишь вырезает свои инструкции, действует как минификатор.
Ну вот что вы все прицепились к терминологии. Какая вообще разница чем является инструмент, если для его использования все-равно одинаково требуется внедрять его в процесс сборки? Ну вот чем идеологически препроцессор лучше компилятора? Или наоборот?
В том-то и дело, что не одинаково. Процессоры, в общем случае, ищут известные им конструкции и заменяют в выходном потоке их на результат обработки (в случае flow и минификаторов на пустую строку, вырезают), игнорируя всё остальное. Компилятор же читает весь входной поток, компилирует его и выводит в выходной результат компиляции. На неизвестной ему конструкции он просто вылетит с ошибкой. Компилятору нужно строгое соответствие входного потока известному ему языку и компилит он в строго указанный таргет. Для flow в babel процессинге я легко могу использовать не то, что stage-0, а свои собственные расширения JS — он их просто проигнорирует, для ts мне придётся переписывать компилятор.
TS точно так же может выплевывать, что получилось, даже если проверка типов не прошла. Или вы про некорректный синтаксис? Ну так любой css-препроцессор точно так же вылетит с ошибкой синтаксиса.

По поводу разницы — я имел в виду, какая вообще разница что происходит внутри процессора или компилятора, если оба принимают что-то на вход и отдают что-то на выход. Какой резон искать плюсы или минусы в различии внутренней реализации?

То, что бабель модульный — не заслуга flow, а заслуга бабеля. А о новом синтаксисе, всяких макросах и метапрограммировании можно вообще бесконечно спорить.
Разница в том, что они выдают на выход. У компилятора есть чёткий таргет для которого он полностью генерирует код, какие-то совпадения с исходным — случайны по сути. Препроцессор же в общем случае лишь преобразует некоторые участки исходного кода, а остальное передаёт как есть.
Спасибо, мне понятно, чем отличается компилятор от препроцессора, и, раз уж мы затронули эту тему, то ts не компилятор, а транспайлер. Но какая разница, если они одинаково встраиваются в процесс сборки?
Разница в том, что они выдают на выход.
Вы хотите сказать, что ни один препроцессор ничего не меняет во входных данных? Может быть, sass? Или stylus? И, наверное, они все молча съедят невалидный синтаксис? Да тот же бабель как миленький грохнется на неправильном объевлении класса. Чем теперь бабель отличается от ts как транспайлер одного языка в другой?
Транспайлер — разновидность компилятора, как по мне. Лет 25 назад курсовик писал «Компилятор Паскаль в Си». Никто на защите не говорил «это не компилятор, это траспайлер».

В том-то и дело, что не одинаково. Вот есть у меня процесс сборки, JS stage 2 плюс декораторы компилируется в JS, поддерживаемый последним Хромом, то есть всё, что поддерживается им, не компилируется. Flow я в этот процесс встрою парой строчек, ts вообще не уверен, что смогу встроить: с одной стороны, вроде нет поддержки JS stage 2 на входе, с другой — нет таргета Chrome 54.

Исходные коды на Flow и на TypeScript мало чем отличаются глобально, часто даже практически одинаково, но вот реализация препроцессора от компилятора имеет большое значение при попытке встроить в существующий процесс, если брать конкретные референсные реализации.
25 лет назад это называлось транслятор, а компилятор — это частный случай транслятора. Сейчас это все модна обзывать новым словом «транспайлер» :)
Вообще считаю, что всем, употребляющим слово «транспайлер», необходимо пройти курс «компиляторов», «компиляторов компиляторов», почитать книгу дракона и т.п. Может быть тогда они перестанут мешать все в одну кучу и будут более взвешенно оценивать разницу в сложности между обработкой произвольного текста регулярными выражениями и контекстно зависимой кодогенерацией.
чистый js с типами в коде

Ага, и JSX не новый язык.

А откуда jsx тут вообще всплыл? Разговор был про ts / flow в качестве валидаторов кода.

Действительно, откуда тут jsx...

jsx / tsx — это всего лишь сахар вокруг ванильного js кода, можно использовать, а можно и нет. Ну и разговор тут таки не про react.

Ts — это всего лишь сахар поверх нативного js. А C++ — это всего лишь сахар поверх C. Я как-то упустил ту грань, которая отделяет "сахар" от нового языка.

flow — это макросы, ts — трансляция в новый вид. Макросы — выкусываются регуляркой с сохранением кода, трансляция — требует кодогенерации. Так понятнее? tsx / jsx — это отдельные языки по сути, никак не связанные с этой веткой комментов.

Точно такой же регуляркой ts превратится в es6. И АСМ в машинный код.


Мне кажется, что "flow — это не язык программирования" — не более, чем маркетинговый буллшит. До тех пор, пока это нельзя будет напрямую запихнуть в интерпретатор js — это будет отдельным языком.


Хотя я, например, жду и надеюсь на появление 'use typed' по аналогии с 'use strict'


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

«Точно такой же регуляркой ts превратится в es6. И АСМ в машинный код»
Не превратится, т.к грамматика, описывающая превращение будет не регулярной, ваш К.О.

А какая критическая разница между кодогенерацией и «выкусыванием»? И то и другое превращает код на одном языке в другой, и то и другое требует процессинга перед подачей в браузер/nodejs.


Вы можете сколько угодно себя убеждать, что flow лучше, но, как и в случае с jsx, это фейсбучное фанбойство субъективно.

А какая критическая разница между кодогенерацией и «выкусыванием»?

Если придираться к словам — никакой. Если не использовать импорт в es2015 стиле, если не использовать модификаторы доступа и т.п. полезные штуки ts — точно так же можно пройтись выкусывалкой типов и получить валидный js-код. Если использовать, то тут все становится гораздо сложнее.
Вы можете сколько угодно себя убеждать, что flow лучше, но, как и в случае с jsx, это фейсбучное фанбойство субъективно.

Что-то тут наркоманией запахло, я уже выше сказал, что не люблю ни ts, ни flow, а люблю ванильный js с jsdoc-валидацией.
Что-то тут наркоманией запахло

Переходите на личности?


а люблю ванильный js с jsdoc-валидацией

Значит, вас двое, потому что пока что я видел только усиленную пропаганду flow. «Шизофренией запахло», выражаясь вашим языком.

Я не являюсь пользователем ни flow, ни ts, мне просто интересно послушать аргументы пользователей ts, почему так и что они думают по этому поводу. А по поводу шизофрении, лучше проконсультироваться с врачом — это скорее шизофазия, когда втыкаются в тред и пытаются писать совершенно о другом, например о jsx и фанбойстве в любом его виде. Так что про переход на личности был немного раньше.
мне просто интересно послушать аргументы пользователей ts, почему так и что они думают по этому поводу
Пробовали в команде и flow, и ts. Остановились на последнем, исключительно из-за, «как бы это сказать и не начать очередной крестовый поход», развитости технологии и удобности. Много чего он все-таки может, чего не может flow. (Справедливости ради, хочу заметить, что есть вещи, которые есть во flow, но до сих пор не сделаны в TS).
Спасибо, комментариев в таком ключе я и ожидал — от тех, кто пользовался и тем и тем. А можно поподробнее, что больше привлекло в ts, кроме модификаторов доступа?
У нас react/redux со всеми вытекающими, и последние фишки (tagged unions, type narrowing) очень сильно помогают. Еще бэкэнд нам генерит типизированное api на основе своих классов. Flow тогда еще не было, так что тогда взяли ts.
Последние наработки TS2.1 облегчают жизнь при составлении типов из других уже существующих вместо обычной копипасты.
А вот type outersection жизненно не хватает для типизации HOC в реакте.
Кто-то плохо учился и не слышал о полиморфизме?

А нужно читать слайд до конца, а не пытаться надувать щеки:

typescript:
let cats: Array<Cat> = []; // can only contain cats
let animals: Array<Animal> = []; // can only contain animals
// wow, works, but is no longer safe
animals = cats;

// because those are now all cool
animals.push(new Dog('Brutus'));
animals.push(new Animal('Twinky'));

// ouch:
cats.forEach(cat => console.log(`Cat: ${cat.name}`));
// Cat: Purry
// Cat: Brutus
// Cat: Twinky

// TypeScript allows for birds and dogs to be cats here :)


flow:
let cats: Array<Cat> = []; // can only contain cats
let animals: Array<Animal> = []; // can only contain animals

// ERROR
// property `purrFactor` of Cat. Property not found in Animal
cats = animals;

// same ERROR
animals = cats;

// End of story for Flow

На мой взгляд, у typescript поведение более логичное для динамического языка.


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


Кроме этого, нужно помнить, что типы в тайпскрипте — это не всегда настоящие типы, а часто лишь декларации — как и в джаваскрипте.

у typescript поведение более логичное для динамического языка.

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

Массивы что в ts, что в flow должны быть одного типа, тут разницы в подходе нет, но в реализации ts есть баг. Если требуется же смешивание типов в массиве — нужно указывать тип any, это штатный способ.

Structural typing (типы одинаковые, если у них одинаковые поля и методы) сделан намеренно. Если бы сделали nominal typing — про нормальное взаимодействие с javascript-кодом можно было бы забыть.


В тайпскрипте очень крутой advanced typing — mapped types, union/intersection, и т.д.

Почему-то с этим не возникает проблем во flow, хотя там не структурное типизирование.

Ну в тайпскрипте тоже со структурной типизацией и отсутствием номинальной проблем не возникает.

Возникает, см. коммент выше с ошибкой приведения типов.

Кто-то придумал синтетический пример, (за такой код в принципе ругать нужно), когда это может не сработать в угоду намеренной особенности языка из-за тяжелого наследства js и требования быть с ним совместимым (скажем так), и все, теперь ts убогий и никуда не годится?


А то, что flow, в случае если у вас есть какая-то большая конфигурация сервиса или библиотеки (highcharts например с его сотнями опций), ничего кроме как any не может сделать и не укажет вам напрямую какая конкретно опция не соответствует ожиданиям этой библиотеки, что превращает весь этот flow в штуку по проверке ошибок там, где обычно их и не допускают — это так, пустяки? Все же я был прав про предвзятость.

Почему синтетический? Это весьма вероятное поведение, когда проверка типов ошибается. Корень проблемы — structural typing, можно наделать бесконечное множество похожих ошибок, главное требование — схожесть структуры типа. Для четкой проверки типов нужно сравнение сигнатуры, а не структуры.

Это пример синтетический, потому что аналогичный код на джаваскрипте работает так же, как и тайпскриптовый, и выдает ошибку в том же месте. Если бы джаваскрипт падал там же, где выдает ошибку флоу, тогда можно было бы говорить, что поведение тайпскрипта некорректно.


Флоу пытается изменить сущность джаваскрипта, тайпскрипт — только привнести опциональные аннотации типов. В этом их разница.

Flow просто более четко следует проверке типов, для чего и были придуманы flow/ts-костыли. Если уж на то пошло — js работает и с кучей ошибок, найденных flow/ts, когда типы не могут быть адекватно выведены — flow/ts в этом случае тоже получается меняют сущность js.
ничего кроме как any не может сделать и не укажет вам напрямую какая конкретно опция не соответствует ожиданиям этой библиотеки, что превращает весь этот flow в штуку по проверке ошибок там, где обычно их и не допускают — это так, пустяки?

Как и ts, к слову. :) Но раз везде натыкано any — в чем смысл чекеров вообще?
все, теперь ts убогий и никуда не годится?

Да почему убогий? Совсем нет, что flow, что ts имеют свои косяки, описанные, кстати, в той же презентации. Я про то, что можно хорошо пролететь при такой проверке, если звезды так сойдутся.
Как и ts, к слову.

Как раз таки нет. Вот буквально вчера пользовался этим: создаю highchart, передавая в качестве аргумента обычную js структуру, без каких-либо типов. И ts мне указал на все параметры, которые не соответствуют тому, что ждет эта библиотека, пройдя по всему этому объекту (а он, на минуточку, собирается из четырех, лежащих в разных местах, файлов через Object.assign()).


(Секунда злости: только вот тайпинги для highchart корявые со всей силы, но это уже не к языку претензии).


если звезды так сойдутся

Это справедливо для каждого языка. В случае с js, так вообще — ts, да хоть flow — глоток свежего воздуха. Там мест, где можно стрельнуть себе в ногу, много. Да еще и асинхронно.

Ну так flow вроде жрет .td.ts тоже (отсюда и описания типов у фейсбучников), так что результат будет тем же. Любой чекер требует валидных описаний типов, потому что по сути это всего лишь контракты, т.е. правила для валидации, а не реальные типы.

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

А можете рассказать, что именно не устраивает? Меня пока-что больше всего бесит отсутствие higher kinded types и type outersection. Хотя обсуждения идут.

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

Не надо быть в тренде. Изучите JavaScript как язык и будет вам хорошо, можете начинать уже с ES6 (с классами).

Статья «почему на TypeScript», в которой упоминаются другие языки, компилируемые в JS по дефолту, и в которой не упомянут Flow, производит впечатление или маркетинговой заказухи, или пропаганды религиозных адептов.

После принятия на JS-проекте решения «нам нужна статическая типизация» основные конкурирующие варианты именно TypeScript и Flow, и неупоминание Flow в ответе на вопрос «почему на TypeScript?» сводит ответ к «почему статическая типизация, максимально совместимая с JS?», но ответа на вопрос «почему на TypeScript?» не даёт. 86,12345% ответа должно содержать ответ на вопрос «Чем и когда TypeScript лучше Flow?»

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

Кто выполнял заказуху? Автор поста? «Питер»? )

Автор поста выполнял внутреннюю заказуху. :) Это же издательство, им тиражи надо раскручивать. Раньше чтобы газет побольше продать, войны устраивали, а теперь вот просто рекламку лабают. :)

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

+1, именно с точки зрения больших проектов. Очень больших.

именно с точки зрения больших проектов. Очень больших.

У вас нет понимания, что такое Очень большой проект. Вот Линукс, Виндовс или поисковая машина гугла это Очень большой проект, то что может понять один человек во всех деталях это ещё совсем не самый большой проект. На фронэнде браузер просто не вынесет «Очень большого проекта» скорее всего.
Sign up to leave a comment.