Typescript 1.8: очень много нового и полезного

    Приветствую, коллеги. В эту пятницу Microsoft выпустила бета-версию Typescript 1.8, в которой на удивление много очень полезных для javascript разработчика штук. Мы в voximplant недавно начали переписывать наш web sdk на typescript, и по моему опыту могу с уверенностью сказать, что польза от компилятора огромная. Он позволяет объединить лучшее, что есть в статически и динамически типизированных языках: в начале вы быстро пишите javascript код, не заботясь о типах и экспериментируя с архитектурой — а когда код «стабилизируется», добавляете типы где считаете нужным и тем самым перекладываете кучу проверок на плечи компилятора. Под катом я кратко пройдусь по ключевым фичам новой версии и поделюсь своими соображениями об их полезности и практической применимости.


    Установка beta-версии


    Если вы добавляете typescript впервые, то достаточно выполнить npm install, указав версию с окончанием @beta:

    npm install typescript@beta --save
    


    Если же вы меняете предыдущую версию на 1.8, то здесь нас ждет небольшая засада: в package.json необходимо прописывать не typescript@beta, а неожиданную ^1.8.0:

      "dependencies": {
        "typescript": "^1.8.0"
      }
    


    Возможность компилировать javascript


    При портировании проектов на typescript много времени отнимает «допиливание» javascript до синтаксиса typescript. Несмотря на то, что typescript «обратно совместим» с javascript, просто так переименность .js файл в .ts не получится: компилятор выдаст ассортимент варнингов и ошибок. Та же история с подключением чужих javascript библиотек: либо .tsd файл, либо приведение к типу any.

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

     tsc --allowJs --outDir out target_file.js
    


    Легковесные компоненты для JSX


    Когда авторы ReactJS сказали что будут запихивать HTML в JavaScript, все долго плевались. Но концепция оказалась на удивление жизнеспособной, и синтаксис прижился. А через некоторое время в facebook заметили, что значительная часть создаваемых компонент довольно легковесны — они рендерят кусочек пользовательского интерфейс на основании своего состояния, и все. Чтобы поддержать благое начинание, в синтаксис JSX была добавлена возможность создавать компоненты на основании функций, а не классов. С новым typescript такой же синтаксис можно использовать и в TSX файлах:

    let SimpleGreeter = ({name = 'world'}) => <div>Hello, {name}</div>;
    


    Кстати, синтаксис JSX теперь подсвечивается в Visual Studio, что делает разработку под Windows еще более удобной. Особенно в свете бесплатной Visual Studio Community Edition. Vim с Emacs конечно рулят, да и WebStorm очень хорош — но Visual Studio это Visual Studio. Штука хорошая и полезная. А начиная с этой версии, компилятор typescript распространяется еще и в виде nuget package.

    Типы как ограничения


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

    function foo<T extends SomeInterface>(arg: T) {}
    


    В новой версии этот механизм существенно расширился: теперь типы могут ссылаться друг на друга, что позволяет описывать сложные ограничения:

    function assign<T extends U, U>(target: T, source: U) {}
    


    Анализ пути выполнения кода


    Новая версия компилятора typescript обучена сообщать о потенциальных проблемах с кодом, когда сам код синтаксически корректен, но программист вряд ли имел в виду ЭТО:

    • Код, который никогда не выполняется (например, код после return).
    • Неиспользуемые метки.
    • Функция, в теле которой есть return, а вот в конце return нет.
    • Забытый break в case.


    Поведение компилятора для этих случаев можно настроить с помощью вот этих параметров.

    Monkey-patch модулей


    В новой версии typescript можно переопределять типы и методы, объявленные в других модулях. Кроме всего прочего это позволяет в несколько строк кода исправлять ошибки в чужих type definition. Да, выглядит как грязный хак, но позволяет быстро написать и проверить код, чтобы потом в спокойной обстановке сделать все по Фен-Шуй:

    import { Observable } from "./observable";
    
    // меняем модуль "./observable"
    declare module "./observable" {
    
        // меняем интерфейс внутри модуля
        interface Observable<T> {
            map<U>(proj: (el: T) => U): Observable<U>;
        }
    }
    


    Допустимые значения для строк


    Самое, на мой взгляд, полезное нововведение. Хорошим тоном считается использовать enum для перечисляемых значений, и ни в коем случае не строки. Почему? Потому что в строковом значении можно допустить опечатку и никто об этом никогда не узнает. Теперь — узнает. Новый typescript позволяет указать какие значения может принимать строка, и проверит ваш код на этапе компиляции:

    interface AnimationOptions {
        deltaX: number;
        deltaY: number;
        easing: "ease-in" | "ease-out" | "ease-in-out";
    }
    


    Замыкание переменной цикла



    Как подсказал в комментариях impwx теперь можно безбоязненно использовать переменую цикла, если она объявлена как let. У нее будет локальная область видимости и каждое замыкание получит свое значение. Этот код выведет цифры от 0 до 4, а не как обычно:

    let list = [];
    for (let i = 0; i < 5; i++) {
        list.push(() => i);
    }
    list.forEach(f => console.log(f()));
    


    Комментарии в tsconfig.json


    Конечно, json с комментриями это уже не совсем json — зато удобно. Очень надеюсь, что то же самое когда-нибудь сделают для npm:

    {
        "compilerOptions": {
            "target": "ES2015", // running on node v5, yaay!
            "sourceMap": true   // makes debugging easier
        },
        /*
         * Excluded files
          */
        "exclude": [
            "file.d.ts"
        ]
    }
    


    Также в меню упрощения для работы с ReactJS, снятия ограничений с --project, цветные сообщения об ошибках, «type guards», проверки для цикла «for...in» и много других небольших доработок, с полным списком которых вы можете ознакомиться здесь.
    Voximplant 186,51
    Облачная телеком-платформа
    Поделиться публикацией
    Комментарии 24
    • +3
      Допустимые значения для строк — огонь! Осталось дождаться, когда народ начнет это использовать в .d.ts.
      • +1
        Еще забыли написать про важную вещь — теперь можно безбоязненно замыкать индекс цикла, объявленный как let.

        let list = [];
        for (let i = 0; i < 5; i++) {
            list.push(() => i);
        }
        
        list.forEach(f => console.log(f()));
        

        А компилируется оно в следующий код:
        var list = [];
        var _loop_1 = function(i) {
            list.push(function () { return i; });
        };
        for (var i = 0; i < 5; i++) {
            _loop_1(i);
        }
        list.forEach(function (f) { return console.log(f()); });
        
        • –1
          А вот и не забыл :) Упомянуто в разделе «мелочи» в конце. Иначе какой-то wall of text получается О_О.
          • +3
            Это не то же самое, что «проверки для цикла «for...in»». То, что область видимости ограничивается только функциями, но не блоками — один из самых досадных багов в архитектуре JS, на который постоянно натыкаются новички. Очень хорошо, что поправили.
            • 0
              А, точняк. Слона-то я и не заметил. Сейчас внесу в статью.
              • 0
                Внес в статью — проверь, пожалуйста.
                • 0
                  Супер, спасибо :)
          • 0
            > Типы как ограничения

            Такое вроде бы обычно называется Generics, «Типы как ограничения» совсем не звучит.
            • +1
              Дженерики и раньше были. А вот ограничений на них не было.
              • +1
                Ограничения тоже были, но раньше нельзя было ограничивать один аргумент-тип с помощью другого из того же набора.
                То есть вот так можно было всегда:

                function test<TA extends SomeA, TB>() { ... }
                

                А теперь можно вот так:

                function test<TA extends TB, TB>() { ... }
                
                • +1
                  Ваша правда, спасибо за поправку.
            • 0
              >Кстати, синтаксис JSX теперь подсвечивается в Visual Studio
              ну вообще, не совсем, в формате ES2015 не подсвечивается. В древнем виде ES3 да
              • 0
                Майкрософтовцы иллюстрируют вот так:

                image
                • +1
                  главное, выбрать удачно картинку для иллюстрации )
                  первый попавшийся пример из гугля github.com/code0wl/react-example-es2015
                  image
                  • 0
                    Судя по тому, как он подсвечивает 'import'ы, тут не все гдадко. Можешь попробовать переименовать .jsx в .tsx?
              • 0
                >Новая версия компилятора typescript обучена сообщать о потенциальных проблемах с кодом.
                >Поведение компилятора для этих случаев можно настроить с помощью вот этих параметров.

                Глобальными параметрами настраивать не круто.
                Круто, как во всяких eslint'ах комментариями к конкретным блокам.
                • 0
                  А разве нельзя сделать локальный файл настроек для проекта? Там же вроде настройки сперва локально ищутся и только потом глобально?
                  • –1
                    Локальный файл настроек для проекта, это всё равно глобальные настройки для проекта :)

                    Хорошо иметь возможность указать: «я вообще-то хочу, чтобы мы указывали, где я забыл break в switch, но вот конкретно в этом месте я сделал это намеренно».
                    • 0
                      В таком ракурсе не рассматривал :(. Для typescript все проверки адекватны, я не могу вот так сходу придумать пример где это будет намереным.
                  • 0
                    Нет, попроектные настройки это тоже правильно. Но исключения на файл/строку тоже нужны.
                  • +1
                    Допустимые значения для строк

                    В этом вашем typescript что enum'ов нету?
                    • 0
                      Есть
                      • 0
                        Тогда весьма странная фича.
                        • 0
                          Это для вещей типа addEventListener и createElement.

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

                    Самое читаемое