Как стать автором
Обновить

Комментарии 56

Пробежался по доке. Ключевые отличия от текущего тайпскрипта (в будущем обещают реализовать):
1. объединения типов
2. как следствие — наличие не nullable типов
3. ветвление по типам
В остальном разница лишь косметическая. Я думаю было бы куда больше пользы, если бы они реализовали какой-нибудь typescript2.0, чем делать несовместимый в мелочах язык.
И, кстати, на typescript код переводить тоже можно постепенно (пофайлово), а вот с typescript на flow — не получится.
И это говорит профессиональный велосипедист? Чем Фейсбук хуже?
Я не говорил, что он хуже. Я говорю, что им с Microsoft стоило бы объединить усилия вместо того, чтобы конкурировать.
Именно. TypeScript от Microsoft, AtScript от Google, Flow от Facebook — все пилят свой собственный JavaScript, с типами и интерфейсами. С минимумом различий. Ну их всех к чёрту, если договориться не могут. С таким подходом буду ждать аннотации типов в самом ECMAScript, тем более с этим, вроде, есть подвижки.
Чем больше крупногабаритных контор набросятся на несчастные типы в JS, тем скорее, я надеюсь, появится новый стандарт.
> пофайлово
Я бы сказал, что нет ограничений и typescript также можно переводить постепенно. Файлы TS могут содержать как TS так и JS код, иногда для этого нужно написать интерфейсы к JS части кода, но это вполне логично и нужно.

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

Например, при переводе какого-то модуля на TS обнаружилась зависимость от модуля $jin.asyn2sync. Чтобы не переводить его сейчас же на TS, я добавил файл с декларацией его апи. В результате при компиляции TS будет проведена проверка типов в соответствии с декларацией. Когда дойдут руки до перевода и этого модуля на TS — декларации и JS будут удалены и заменены на нормальную TS-реализацию.

А Flow вообще ничего не поймет, потому, что функция в том модуле создаётся через указание полного пути литералом.
А вы пробовали Flow, или просто так говорите? Мой поинт в том, что Flow позволяет делать то, что вы описали без .d.ts как раз.
Я пролистнул документацию и не нашёл там даже поддержки jsdoc, а значит вытащить такие идиомы как «класс» или даже «примесь» из js кода он не сможет, потому, что в js есть тысяча и один способ реализовать наследование.
Все это пустые слова. Покажите код, посмотрим как отреагирует Flow.
Вместо тупой проверки типа переменной, Flow учитывает природу динамической типизации.
/* @flow */
function Foo (a) {
    this.a = a;
}

Foo.prototype.bar = function(foo) {
    console.log(foo.a);
}

var foo = new Foo(1);
foo.bar(foo);
foo.bar(123);

Вот вам пример, ошибка где надо — последняя строка
PS у меня есть предположение, что ядром flow является VM, а значит анализируется рантайм, а значит вы любым способом можете определить/унаследовать класс, а Flow все отследит. Только вот не могу ничем подтвердить догадки
function define( config ){
	for( var path in config ) {
		var current = this
		var names = path.split( '.' )
		for( var i = 0 ; i < names.length - 1 ; ++i ) {
			var name = names[ i ] || 'prototype'
			var next = current[ name ]
			if( !next ) next = current[ name ] = {}
			current = next
		}
		current[ names[ i ] || 'prototype' ] = config[ path ]
	}
}

define({
	'Foo' : function Foo (a) {
	    this.a = a;
	},
	'Foo..bar' : function(foo) {
	    console.log(foo.a);
	}
})

var foo = new Foo(1);
foo.bar(foo);
foo.bar(123);
/Users/nkt/Desktop/flow.js:9:24,38: access of computed property/element
Computed property/element cannot be accessed on global object

/Users/nkt/Desktop/flow.js:10:32,51: assignment of computed property/element
Computed property/element cannot be assigned on global object

/Users/nkt/Desktop/flow.js:13:9,61: assignment of computed property/element
Computed property/element cannot be assigned on global object

/Users/nkt/Desktop/flow.js:26:15,17: identifier Foo
Unknown global name

Found 4 errors

Если this в define заменить на window — останется неизвестный Foo.
Будем ждать, что уж. Тут наверное тот случай, когда можно подсказать компилятору.
Плюс, на сколько я понимаю, Flow заточен под использование с ES6 и наверное не будет развивать es5, что в принципе логично.
Сравнение с typescript немного некорректно. Его придется компилировать, а парни из Flow утверждают, что все пашет само прямо на живую. Что несомненно лучше. Единственное, на мой взгляд, лучше бы они JSDoc начали парсить, чем вводить какие-то навороты по синтаксису, а то и правда TS получается.
ИМХО, с внедрением статической типизацией сейчас такой же дикий запад, как был в свое время с модульными системами. Когда каждый писал свой велосипед и было неизвестно доживет ли система модулей хотя бы до следующего года. Сейчас все более-менее устаканилось: browserify, webpack, requirejs. Еще одна реализация системы типов, это ок. Посмотрим какая из них реально выживет и в итоге станет стандартом. Пока что Flow это очень умный препроцессор, который почти не меняет исходный js-код. Посмотрим, что будет по мере добавления фич.
Flow это не просто препроцессор, это скорее статический анализатор. То есть его изначально можно использовать только как статический анализатор, по моему это очень круто.
В отличии от TypeScript, есть возможность делать код типизированным постепенно.

Что вы имели в виду под этой фразой? В Typescript можно просто поменять расширение с js на ts и всё будет компилироваться, поскольку Typescript — надмножество Javascript. А потом постепенно заменять типы — например, начиная с локальных переменных.
Судя по каментам автор имел ввиду что typescript, при простом изменении расширения будет воспринимать код как обычный js, при этом будет игнорировать опасные моменты (например var q = 5; w = «5»; e = q + w;), flow же в такой ситуации выдаст предупреждение что проходит не математическая операция а конкатенация (типы не совместимы). Как-то так. Естественно оба языка будут работать с чистым js без проблем, они оба полностью совместимы с js.
Typescript на само сложение не ругается, подразумевая конкатенацию. Однако тип всех переменных выведет и вызвать на переменной e метод типа number не даст.
Да, вы правы.
taliban вы тоже правы, поправил начало.
Вы, мягко выражаясь, не правы

function foo( a ){
    alert( a || 'hello world' )
}
foo() // error TS2081: Supplied parameters do not match any signature of call target.
Да, прошу прощения — вы правы, количество аргументов проверяется жестко. Но какой вариант логичнее с точки зрения статической проверки — показывать ошибку или нет — однозначно сказать нельзя. Всё зависит от того, как используется a внутри функции.

В Typescript есть еще одна запрещенная конструкция, которая является допустимой в JS:

var x = "test";
x = 123;

Было бы интересно узнать, что Flow говорит в обоих этих случаях.
Для вызова функции без аргумента — Too few arguments (expected default/rest parameters in function)
Если изменить сигнатуру на function foo(a = undefined) — нет ошибок, es6 такое позволяет
Для второго примера все ок.
Более того
/* @flow */
var x = "test";
x.indexOf('t');
x = 123;
x.indexOf('t');

Ошибка на последней строке. Вот этим Flow и лучше TypeScript.
Не сказал бы, что лучше. Обычно типы переменных не должны меняться. К тому же, что он выдаст на такое?

/* @flow */
var x = "test";
x.indexOf('t');
while( o > 1 ) {
    o--;
    x = Math.random();
}
x.toFixed( 2 );
/Users/nkt/Desktop/flow.js:5:8,8: identifier o
Unknown global name

/Users/nkt/Desktop/flow.js:9:1,14: call of method toFixed
Property not found in

А вот так ошибок нет:
/* @flow */
var x = "test";
x.indexOf('t');
var o = 0;
do {
    o--;
    x = Math.random();
}while( o > 1 );
x.toFixed( 2 );
function( o ){
    var x = "test";
    x.indexOf('t'); 
    while( o > 1 ) {
        o--;
        x = Math.random();
    }
    x.toFixed( 2 );
}
У вас ус отклеился)
Если добавить название функции:
/Users/nkt/Desktop/flow.js:9:5,18: call of method toFixed
Property not found
Ну то есть он не замечает, что в цикле тип меняется.
Лучше? Я бы наоборот назвал это фундаментальным недостатком :)

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

Кстати, а если переопределять значение переменной в цикле, что оно будет делать?
var data = ["123", 123];
var elem;
for(var i =0; i < data.length; i++)
{
    elem = data[i];
    elem.indexOf('1'); // что-то будет?
}


UPD: Товарищ vintage меня опередил, даже пример практически такой же.
Нет, так ошибок нет.
На самом деле лучше бы в typescript добавил то что им не хватает. Выведение типов без аннотаций умеет google closure компилятор.
Я так понял, парням из фейсбука в typescript не понравилось то, что там нету ихнего безумного JSX. А гуглю с аналогичной поделкой — видимо что-то не хватает для из ангуляра. Может тоже строго-типизированного HTML, лол. Обоим двум пока не хватает мозга понять что JSX и Angular нужно просто выкинуть в помойку как побочные эффекты эволюции.

Но нет — будет у нас ещё два тайпскрипта. Привычно, в очередной раз, вздыхаем, берем попкорн и смотрим шоу. Которое, разумеется, закончится тем, что JSX и ангуляр выкинут в помойку.
JSX и ангуляр выкинут в помойку

Это вы как себе представляете? А на чем приложения писать на чистом js?
На реакте и его потомках. Если ничего лучше не придумают.

На то что подход ангуляра не взлетел и больше никогда не взлетит — коственно указывает практически полное отсутствие под него компонентов и остальной инфраструктуры, то что гугль сам его толком не использует, ну и просто здравый смысл.
Эм. Под ангуляр куча компонентов, это не армия jquery плагинов, но тоже достаточно для среднестатистического проекта. React — это только view, не стоит про это забывать.
AngularJS отлично дружит с Typescript. Единственное, чего пока не видел — качественного автодополнения кода в выражениях типа ng-repeat.
До реорганизации хабра, я имел возможность к статьям ни о чем оставлять камменты, и из разговоров иной раз выносить какие-то мысли. Иногда набрасывал, иногда — давал какие-то советы, иногда — помогали мне. Было в целом прикольно, и у меня был даже какой-то положительный бюджет в карме, который я воспринимал чисто как автомагическое определение моего поведение как нормального человека — не тролля и не спаммера.

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

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

За сим — откланиваюсь.
Как по мне, так google closure compiler с его прекрасной системой аннотирования. Которые совместно не только позволяют искать ошибки на ранних стадиях, но и ужимать скрипты, повышать производительность и даже использовать информацию о типах при оптимизации — вещь на много более полезная. К сожалению стилистические требования для продвинутой оптимизации немного высоковаты, что не позволяет его широко использовать.
Но мне нравится направление движения сообщества, которому не хватает типов в таком типо-разгильдяйском языке как JS.
Я имею большой опыт разработки на closure compiler и могу сказать что писать для closure компилятора довольно утомительно, код получается зашумлённый и нужнопостоянно проверять что написанный код в Release режиме работает так же как в debug`е. Есть загнувшийся проект который модифицировал typescript компилятор таким образом что он при компиляции выдавал js код с аннотациями для closure компилятора, я считаю была очень здравая мысль, жалко не развилась.
По поводу утомительно и зашумлено — не согласен. То же самое, что и на других языках типа java. Иногда бывают танци-с-бубном для избавления от варнингов, но при некотором опыте — это не напрягает.
По поводу проверки работает ли оптимизированный код — так я последний раз сталкивался год назад с тем, что компилятор не то «нагеморировал». И то в не совсем красивом инжекшине.

По поводу тайпскрипта — это да, это очень приятно. Но у меня опустились руки, как только подумал сколько времени нужно потратить, дабы преобразовать саурсмап от откомпилированного google closure compiler результата через саурсмапы тайпскрипта к исходникам…
Я не знаю как в сравнении с Java, но в сравнении с C# анотации closure это очень утомительно и не читабельно.
По поводу нагеморировал, вот такой код
/** param {Object} a */
function(a){
var x = a.someField;
}

closure в advanced режиме преобразует в такой
function(a){
var x = a.mangledField;
}

Спрашивает, какого он переименовал поле, если не имеет о нём никакой информации?
«Спрашивает» -> «Спрашивается»
>… но в сравнении с C# аннотации closure это очень утомительно и не читабельно.
Возможно XML Comments чем-то лучше чем javaDoc формат, но как по мне это не принципиально. (Только не скажите, что доки писать вредно!)

Прошу прощения за оффтопик
/** @param {Object} a */
function(a){
var x = a['someField'];
}
//Преобразует к 
function(a){
var x = a.someField;
}

Не стоит жаловаться на переименование свойств, если используете адвансед оптимизацию — пишите просто верно. Ко всему прочему есть различные externs, publish итп. В привате отвечу на вопросы, а тут не стоит засорять.
Я лично именно за такой вариант, когда при применении RequireJS (ну или Cajon, если CommonJS синтаксис ближе) есть и модульность и нет необходимости что-то компилировать перед запуском, а парсинг JSDoc обеспечивает более-менее вменяемое автодополнение при написании и подсветку потенциально опасных мест. На мой взгляд, компилировать язык, который без этого прекрасно обходится — есть безусловное зло. Только при создании билда на продакшен, там это оправдано.

Сейчас IntelliJ продукты (WebStorm, IDEA и т.д.) прекрасно справляются с большинством ситуаций при наличии хорошо написанного JSDoc.

Надеюсь это направление со временем не отомрет и JS не придет к тому, что любой чих надо будет перекомпилировать…
Это конечно очень приятно, и в целом я с Вами согласен.

А в дебаг режиме не нужно ничего компилировать, оно и так работает как RequireJS.

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

> Надеюсь это направление со временем не отомрет и JS не придет к тому, что любой чих надо будет перекомпилировать…
Не отомрет! ;)
(Только привычка разработки методом, близким к «научному тыку», как правило менее качественна, чем у людей, которые не могут себе позволить, даже в голове, писать маленькими не полными кусочками. Парадокс, но я к примеру делаю больше ошибок в примитивных для-себя страничках на jQuery, чем на ассемблере 20 лет назад.)
А в дебаг режиме не нужно ничего компилировать, оно и так работает как RequireJS.

Если Вы про google closure compiler, то да, а вот фишки Flow, в которых задействовано в т.ч. расширение синтаксиса, без пре-компиляции не взлетят. Так что я сугубо за подход через аннотации в комментариях.

Что касается проектов с мегабайтами исходников — для таких только бандлы функциональности, загружаемые по мере надобности, и, безусловно, прекомпиляция, но так то ж для продакшена. Там можно многое, чего не желательно иметь в дев режиме. И наоборот…
Да ладно, прекрасно? Разве в таком коде:
/** param {Array.} strings
function(strings) {
var s = _.find(function(str){ return str.indexOf('s') != -1;})
}

редактор сможет дать адекватную подсказку для методов имеющихся у str?
Я боюсь это пример того случая, в котором редактор будет жестоко тупить…
Вместе с spyjs/COLT сможет.
А как код потом выполняется?
Насколько я знаю JS (а я знаю я его по Qt Quick только), там нельзя указывать типы подобным образом…
Пишите в начале файлик с типами через function (a: number, b: string), а дальше препроцессор прогоняет ваш файлик и вырезает все ваши указания типов, оставляя function(a, b)
Это грустно как-то, по сути в системе контроля версий имеешь неработоспособный код, который перед использованием еще нужно чем-то прогонять =\
Для js — это вполне нормальная ситуация. Клиентские приложения часто пишутся с использованием например commonjs, сборка в этом случае все равно требуется, несмотря на то что сам код написан на чистом js.
а в c++ не так? Перед использованием, код нужно прогнать компилятором)
Не совсем компилятором, конечно, но в общем и целом Ваш пример вполне себе корректный!
Да, приходится признать, что отсутствие опыта программирования на JS в самом привычном его варианте использования заставляет мыслить иначе)
Вместо того, чтобы изменить сам язык, лучше сделать невозможное и написать для него статический анализатор :)
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.