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

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

Это упрощает запуск JavaScript кода в браузере или при работе Node.js. Однако он уязвим для многочисленных ошибок во время исполнения (рантайм), которые могут испортить ваш пользовательский опыт использования.



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

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

Flow, TypeScript или ReasonML

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

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

И тем не менее Flow и TypeScript не дают 100% безопасности при добавлении типизации в коде.
По этой причине идеальная безопасность типизации достигается посредством вывода и делает аннотирующие переменные и сигнатуры функций более простыми.

Простые и явно надуманные примеры

Рассмотрим следующий код:

let add = (a, b) => a + b;

В обычном JavaScript эти аргументы могут быть числами или строками. В TypeScript или Flow эти аргументы могут быть аннотированы как:

let add = (a: number, b: number) => a + b

Теперь мы видимо задаем именно два int значения. Не два float или два string, для их операций сложения используются другие операторы.

А теперь давайте взглянем на чуть измененный пример в Reason:

let add = (a: string, b: number) => a + b
add('some string', 5) // outputs: "some string5"

Эта функция работает! И это может показаться удивительным. Каким образом Reason это понимает?

let add = (a, b) => a + b;
add("some string", 5);
/*
This has type:
  string
but somewhere wanted:
  int
*/

Эта функция имела недостатки на уровне реализации. У Reason есть разные операторы для сложения int, float и string.

Цель этого простого примера – показать, что вполне возможно иметь лишние «ошибки» типов, даже если это не роняет приложение.

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

Опыт разработчика

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

Это одна из областей, где TypeScript имеет преимущество над Reason, поскольку программа на TypeScript не должна быть скомпилирована идеально, чтобы предложить автозаполнение. Reason же заставит вас исправить все ошибки в синтаксисе и типах, перед тем, как предложить вам полезное исправление.

Так это работает в VSCode, но я знаю много Reason разработчиков, которые используют vim. Здесь мы не будем углубляться в сравнения.

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

С другой стороны, Reason сложнее использовать, поскольку для него доступно меньше статей и документации. Надеюсь, с его развитием это исправится.

Если Reason вас заинтересует, вы можете найти здесь его документацию. А еще подпишитесь на таких личностей, как @jordwalke, @jaredforsyth и @sgrove в Твиттере. Они многое могут рассказать об экосистеме Reason/OCaml.

Если же вы захотите узнать, как Reason работает с GraphQL, обратитесь к другой моей статье «Reason with GraphQL, the Future of Type-Safe Web Applications».

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

Комментарии (68)


  1. lightmaann
    20.03.2019 17:54
    +1

    Разве «система типов улучшает ваш код на JavaScript» — не является само собой разумеющимся?)


    1. VolCh
      20.03.2019 18:01
      +4

      Нет. Она может его испортить и вообще не даст собрать хоть как-то работающее приложение :)


      1. shai_hulud
        20.03.2019 18:11

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


      1. serf
        20.03.2019 23:56

        Скрипт кидди детектед.

        PS При использовании лок файлов зависимостей сборка того же самого кода должна проходить успешно тк typescript компилятор остается тот же.


        1. justboris
          21.03.2019 15:12
          +1

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


          P.S. а что вы имели в виду под "скрипт кидди"? Я знаю только вот это определение и оно тут не при чем.


          1. serf
            21.03.2019 15:38

            Имелось в виду, что если начать покрывать работающий код типами, то можно погрязнуть в куче разных несовпадений типов, которые пользователям никак не мешали, но компиляцию typescript ломают.
            Можно на этап перевода кода разрешить работать с JS файлами (опция allowJs) и так постепенно по файлу переводить на TS. Или что-то такое испольовать github.com/evolution-gaming/tsc-silent

            P.S. а что вы имели в виду под «скрипт кидди»? Я знаю только вот это определение и оно тут не при чем.
            Могу перефразировать — код манки.


            1. justboris
              21.03.2019 15:55

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


              1. serf
                21.03.2019 16:03

                Речь ведь идет только о переходном периоде который неизбежен если происходит перевод JS кода на TS. Any расставить можно, но это муторно. Можно включить на время allow implicit any, но мне такой вариант не нравится.


  1. VolCh
    20.03.2019 18:00
    +5

    Теперь мы видимо задаем именно два int значения. Не два float или два string

    number в javascript и typescript — именно, что float (если быть совсем точным, то double, если использовать сишные типы). В них нет int типа.


  1. Akuma
    20.03.2019 18:45
    +3

    Не так давно пытался внедрить в уже работающий проект Flow… ну так себе затея, в итоге отказался. Сторонние библиотеки не типизированы, твои типизированы, все это мешается друг с другом… уф, жесть какая-то была.

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


    1. sanchezzzhak
      20.03.2019 20:39

      Это полный переписон проекта..


    1. serf
      20.03.2019 23:57
      +1

      Flow можно считать мертв, зачем почивших тревожить.


      1. Akuma
        20.03.2019 23:59

        1. Он упоминается в статье.
        2. Это было давно (достаточно давно для Flow и не так давно для проекта).


    1. worldxaker
      21.03.2019 11:25

      мы потихоньку внедряем в большой проект ts и никаких особых проблем с этим не возникает


      1. Akuma
        21.03.2019 14:07

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

        Сейчас так же?


        1. serf
          21.03.2019 14:44

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


        1. worldxaker
          23.03.2019 00:42

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


  1. ecmaeology
    20.03.2019 19:02
    +3

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

    Ваша IDE не знает, какие методы и свойства доступны для модулей и библиотек
    IDE плохо работает?


    1. morsic
      22.03.2019 10:40

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


  1. stardust_kid
    20.03.2019 19:19
    +1

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


    1. Aquahawk
      20.03.2019 19:50
      +3

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


      1. serf
        21.03.2019 00:01

        А можно раскладку по коду запуском этой утилиты github.com/cgag/loc?


        1. Aquahawk
          21.03.2019 11:19

          --------------------------------------------------------------------------------
           Language             Files        Lines        Blank      Comment         Code
          --------------------------------------------------------------------------------
           JSON                     1       615299            0            0       615299
           TypeScript            3587       662204        21297       150768       490139
           HTML                    16         1868          222            7         1639
           JavaScript               7         1583          401          186          996
           C                        1          703           95           28          580
           Plain Text              24          740          228            0          512
           CSS                      8          332            8           22          302
           C/C++ Header             1           39            9            3           27
           Bourne Shell             1            7            1            1            5
          --------------------------------------------------------------------------------
           Total                 3646      1282775        22261       151015      1109499
          --------------------------------------------------------------------------------

          Единственный Json кстати это набор данных для теста одной хреновины, так что да, объём кода завышен в мегабайтах.


          1. serf
            21.03.2019 11:23

            490139 строк TS кода, прилично. Сколько народа в команде и начинался ли проект изначально на TS или переводили с JS?


            1. Aquahawk
              21.03.2019 11:25

              Проекту скоро 8 лет, изначально писан на actionscript 3.0. Там очень похоже всё на тайпскрипт. Мы написали транспилятор as3->ts и сейчас полируем всё для запуска. Да масштаб проекта позволяет окупить написание транспилятора и рантайма флеша только ради одного этого проекта. Это большая и старая flash игра, имеющая огромную армию фанатов. Это естественно чистые src, без node_modules и прочей шелухи. Из забавного, в проекте около 15 тыс игровых объектов, с артом, поведением и т.д. Даже typescript форкнуть пришлось :)


              1. serf
                21.03.2019 14:54

                Это все веб код или под ноду? Страшно предположить сколько весит итоговый собранный бандл. Может вам сразу на Rust переписать и в webassembly перегнать потом.


                1. Aquahawk
                  21.03.2019 16:55

                  Это клиент, с конвертером, там метров 15 js сбилженного, это без сжатия, для игры ок, вариантов нет, во флеше это было два мегабайта


      1. VolCh
        21.03.2019 11:17

        Где-то помогает, где-то мешает. Одно из основных рекламируемых достоинств — простой рефакторинг — на практике часто не работает, по крайней мере в IDE от JetBrains. А с почти всем остальным навскидку и тесты справиться могут и просто статанализ без явной типизации.


        1. Aquahawk
          21.03.2019 11:22

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


          1. VolCh
            21.03.2019 12:33

            Вот подсказки далеко не всегда нормальные. Особенно в случае каких-то JS библиотек (банальный jQuery) с декларациями, когда они активно используют «параметрический полиморфизм», приводящий к семиэтажным описаниям типов, а родные человекопонятные jsdoc игнорируются.


      1. stardust_kid
        21.03.2019 21:11

        Жаль вас разочаровывать, но многие большие кодовые базы на js написаны без типизации.


        1. justboris
          21.03.2019 21:40

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


        1. Aquahawk
          22.03.2019 09:06

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


    1. serf
      21.03.2019 00:00

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


      1. SergeyEgorov
        21.03.2019 09:57

        А почему дизайн системы отсутствует при использовании голого JS?


        Неужели использование TS автоматически привносит в проект хороший дизайн?


        1. serf
          21.03.2019 10:30

          Очевидно потому что там нету типизации и нет интерфейсов, модель данных не опишешь и не зашаришь ее по всему приложению, между сигнатурами всех функций/методов/конструкторов. В сыром JS приоритет направлен на сговнякать что-то по быстрому и в продакшен. В сыром JS дизайн если он изначально в каком-то виде существовал, то очень быстро обесценивается тк он не подкреплен компилятором. Это как издать указы и потом просто считать и народ убеждать что они уже исполнены (реальный случай). Достаточно запустить например статический анализатор JS кода на любом более-менее крупном проекте и почти наверняка там вылезут грабли, как например самый частый случай — передача лишних аргументов в функции/методы. А если речь идет о разработке библиотек или взаимодействии между разными системами, то сырой JS использовать вообще вредно когда существует TS.


          1. SergeyEgorov
            21.03.2019 10:54

            А куда девалась типизация в JS? Почему вы считаете что в JS нет интерфейсов?


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

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


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


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


            1. serf
              21.03.2019 10:58

              Очевидно имелся ввиду дизайн модели данных/типов, сигнатур методов.

              Почему вы считаете что в JS нет интерфейсов?
              Уже появились?
              Ну то есть к примеру возьмем мы и напишем программу на языке C, запустим компилятор и он ее откомпилирует успешно. Означает ли это автоматически наличие хорошего дизайна в компилируемой программе?
              Это означает что сигнатуры и типы не поломаны.


              1. SergeyEgorov
                21.03.2019 11:09

                Уже появились?

                Никуда и не девались.


                function FirstStringFactory() {
                    this.make = function() {
                        return "First";
                    };
                }
                
                function SecondStringFactory() {
                    this.make = function() {
                        return "Second";
                    };
                }
                
                function StringFactoryConsumer(stringFactory){
                    this.name = stringFactory.make();
                }

                Это означает что сигнатуры и типы не поломаны.

                Вы считаете что это основа дизайна?


                1. serf
                  21.03.2019 11:19

                  Вы считаете что это основа дизайна?
                  Неотемлемая часть.
                  Никуда и не девались.
                  Это не интерфейсы но их реализация в виде абстрактных классов. TS позволяет описать StringFactoryConsumer и stringFactory без их реализации.


                  1. SergeyEgorov
                    21.03.2019 14:38

                    Вы считаете что это основа дизайна?
                    Неотемлемая часть.

                    Странный ответ. А что еще в процессе разработки имеет отношение к дизайну, ну кроме компилятора?


                    Это не интерфейсы но их реализация в виде абстрактных классов. TS позволяет описать StringFactoryConsumer и stringFactory без их реализации.

                    Почему абстрактных?


                    1. serf
                      21.03.2019 14:50

                      Не станут вас в чем-то убеждать. Если комфортно работать с сырым JS, то пожалуйста, каждому свое. Ваши выдуманные «интерфейсы в JS» таковыми не являютсят. В JS нет возможности гарантировать что в StringFactoryConsumer прилетит аргументом именно экземпляр FirstStringFactory или SecondStringFactory. Это просто бесполезный рантайм оверхед.


                      1. VolCh
                        21.03.2019 15:10

                        В TypeScript таких гарантий тоже нет. Максимум он гарантирует, что прилетит что-то, у чего есть метод make, возвращающий строку. Что у класса, описывающего прототип прилетевшего объекта будет implements stringFactory он не гарантирует


                        1. serf
                          21.03.2019 15:32

                          Не гарантирует потому что TS это структурная штука, которая не существует в рантайме, это by design. То есть да гарантируется только совпадение структуры. Этого как правило достаточно при работе со своим кодом. А при работе с внешними/сомнительными данными все равно применяется рантайм валидация.


              1. VolCh
                21.03.2019 11:12

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


                1. serf
                  21.03.2019 11:21

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


                  1. VolCh
                    21.03.2019 12:36

                    В TS они неполноценные и польза от этого существенно снижается. Банальный this instance of ISomeDoable не работает.


                    1. serf
                      21.03.2019 13:41

                      Не нужно притягивать слона за уши. Польза по сравнению с чем? В контексте обсуждения сравнивать есть смысл только JS с TS.


                      1. VolCh
                        21.03.2019 15:13

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


                        1. serf
                          21.03.2019 15:32

                          На самом деле интерфейсы в TS не особенно и часто используются.


                          1. VolCh
                            22.03.2019 10:43

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


                            1. serf
                              22.03.2019 10:44

                              Полагаю это все же TS типы а не интерфейсы.


      1. stardust_kid
        21.03.2019 21:12

        Звучит как Golden hammer.


  1. qbz
    20.03.2019 23:12

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


    Как можно так тупить?
    https://flow.org/try/#0MYewdgzgLgBAHjAvDAFGAXDMBXAtgIwFMAnGAHxmmIEswBzASkxwJKQD4YBvAKAEhQkWNQgA5PEVLIoATwAOhEADMsSRMgDkLSRv7UVKEeNbEG3fn2KEo2YmFUAqGAEYADPwC+-KzbswATO5ePDxwKM4MQA


    Или вот так?
    https://flow.org/try/#0NoRgNAdgrgNjYGYC6A6AZgSxgFwKYCcAKCAAgF4A+E7ATwAdcB7NE0s9kgcmgFsAjApwCUKHgEM6xclVIAqEiAAMQoA


    1. serf
      21.03.2019 00:40

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


      1. qbz
        21.03.2019 04:29

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


        1. Cerberuser
          21.03.2019 06:41

          Скажите это пользователям GMail...


        1. serf
          21.03.2019 07:24

          Допускаю что я перепутал фейсбука с гуглом, они для меня на одно лицо.


    1. justboris
      21.03.2019 12:29

      Если вам это как-то поможет, то второй пример нормально работает в typesctipt. Ссылка на демо.


      Первый ломается и там, и там, но его можно пофиксить, перенеся проверку typeof напрямую в if. Ссылка на демо.


      1. qbz
        21.03.2019 16:38

        Спасибо, да, как раз хочу попробовать пересесть на TS и забыть про flow раз и навсегда. Насчет переноса условия прямо внутрь if я в курсе, но в этом и суть — бывают условия громоздкие или не совсем очевидные, в таком случае надо их выносить в отдельную переменную с ясным названием, которая позволит просто читать и понимать такой код.


        1. justboris
          21.03.2019 16:43

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


          Еще посмотрите на type guards. Можно вынести код проверки типа в отдельную функцию, и typescript поймет, что там проверяются типы.


          1. qbz
            21.03.2019 16:50

            Интересно, гляну. Насчет переменных — так все равно же у них есть какой-то кеш или что-то вроде infer tree для такого. Почему бы переменные в ифе тоже не расчитывать..?


  1. bugsfinder
    21.03.2019 09:11

    Если Reason вас заинтересует, вы можете найти здесь его документацию.
    Не найти там ничего — 404
    There isn't a GitHub Pages site here.


    1. bugsfinder
      21.03.2019 09:16

      Должна быть ссылка: https://reasonml.github.io


  1. gearbox
    21.03.2019 10:40

    Не знаком с reason, но то что я увидел на сайте наводит на мысль что он тут в списке лишний. Flow и Typescript это система типов прикрученная к js, при этом это остается js, просто с типами. reason больше похож на ocaml компиляющий в js, ну так тут таких сотни. Правильнее его сравнивать с purescript и ему подобными.


    1. serf
      21.03.2019 11:01

      Здравая мысль. А то нас здесь пытаются сбить с толку.


  1. dolovar
    21.03.2019 11:25

    Краткое содержание:

    Как система типов улучшает ваш код (обещание ответить на вопрос).
    Ванильный не типизирован (совсем?), что упрощает разработку, но добавляет уязвимости.
    Варианты проблем: поле отсутствует в записи (объекте?), метода нет в экземпляре класса (объекте?), IDE не всё может автодополнять (никто не сможет).
    Можно попробовать Flow или TypeScript, но они громоздкие, сложные, загружающие и не дают 100% гарантии безопасности (никто не даст).
    Рассмотрим два с половиной примера, в которых внезапно появляется Reason, который позволяет сложить строку с числом.
    Упомянут опыт разработчика-фаната, который не будет сравнивать VSCode и vim и надеется на появление документации.
    Про Reason можно узнать там-то. И про GraphQL. И про день открытых дверей.
    Не нашел ответ на вопрос в заголовке. Плохо искал, наверное.


    1. serf
      21.03.2019 11:28

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