Занимательная история от Феликса Ризеберга, разработчика в Slack, о том как они используют JavaScript, почему перешли на TypeScript и какие подводные камни встретились на их пути.
![](https://habrastorage.org/files/3b5/444/efc/3b5444efccf746c49ed5622f48735684.jpg)
Когда Брендан Айх (Brendan Eich) создал самую первую версию JavaScript для Netscape Navigator 2.0 всего за десять дней, он, скорее всего, не ожидал, что его изобретение будет настолько применяться в классическом приложении Slack. Мы используем единую базу кода JavaScript для создания многопоточного классического приложения для ОС Windows, MacOS и Linux, которое осуществляет постоянное взаимодействие с машинным кодом.
Управление большими базами кода JavaScript представляется довольно сложной задачей. Каждый раз при передаче объектов из JavaScript в Chrome в Objective-C отдельные части кода должны гарантированно соответствовать друг другу для получения обратного вызова в другом потоке Node.js. В настольных операционных системах самая незначительная ошибка может привести к сбою приложения. Именно по этой причине мы внедрили TypeScript (расширенный набор кодов JavaScript на основе статических типов), после чего быстро научились не беспокоиться и доверять компиляторам. И мы не одиноки. По результатам опроса разработчиков StackOverflow в 2017 году TypeScript занял почетное третье место среди самых популярных технологий программирования. Учитывая, насколько быстро статическая проверка типов набирает популярность, нам хотелось бы поделиться своими знаниями и практическим опытом.
В прошлом мы применяли JSDoc для документирования подписей функций, а также использовали комментарии, чтобы сообщить разработчикам о цели и назначении классов, функций и переменных. В этом тоже есть свои минусы. С первого взгляда на код трудно понять, к чему приведет использование JavaScript. Приходится просто верить на слово, что разработчик, написавший этот код, правильно его задокументировал, и все те, кто впоследствии этот код изменял, должным образом отразили эти изменения в документации. В сложных системах, включающих множество модулей и зависимостей, можно запросто получить сбой в функции, даже не открывая файл, в котором она содержится.
Чтобы как-то улучшить ситуацию, мы решили попробовать применить статическую проверку типов. Инструмент статической проверки типов не изменяет поведение кода в среде выполнения. Вместо этого он анализирует код и пытается по возможности логически выводить типы, направляя разработчику предупреждение перед отсылкой кода. Инструмент статической проверки типов понимает, что
![](https://habrastorage.org/files/574/71c/c78/57471cc7809d4e678105d825bf16f5ec.png)
Иными словами, при использовании такого инструмента обеспечивается поддержка системы за счет объявления типов вручную, что позволяет сообщать о предполагаемом поведении программы как пользователю, так и компьютеру. Приведенный ниже код определяет интерфейс для объекта «user» и метода, который предположительно должен получать данные о возрасте пользователя. Инструмент статической проверки типов анализирует этот код и предупреждает о стандартных ошибках (например, когда пользователь ожидает, что неопределенное свойство всегда будет доступно).
![](https://habrastorage.org/files/41b/d89/c5d/41bd89c5d7944c0d9a6589f8b313d8d5.png)
Что интересно, код не изменяется в среде выполнения: инструмент статической проверки типов не предоставляет служебных данных конечному пользователю. Приведенный выше пример в среде выполнения выглядит как совершенно стандартный код JavaScript:
![](https://habrastorage.org/files/30a/75e/d55/30a75ed557ab44039d29dc11e8a59f2e.png)
Интеллектуальный инструмент статической проверки типов позволяет быть уверенным в качестве кода, дает возможность выявлять типичные ошибки еще до того, как они совершены, а также обеспечивает автоматизированное документирование базы кода.
Мы решили использовать программу TypeScript корпорации Microsoft, в которой функции статического анализа типов объединены с компилятором. Современный JavaScript — это действующий TypeScript, то есть TypeScript можно использовать, не меняя ни одной строчки кода. Таким образом, можно использовать «постепенную типизацию», чтобы выполнять компиляцию и статический анализ на ранних этапах и не прерывать работу на время устранения критических ошибок или добавления новых функций.
На практике включение функций анализа и компилятора без изменения кода означает, что TypeScript сразу же пытается проанализировать и расшифровать код. Программа использует встроенные типы и определения типов, доступные для сторонних зависимостей, чтобы проанализировать поток кода и выявить те мелкие ошибки, которые ранее остались незамеченными. Если программе TypeScript не удалось успешно расшифровать код, она относит его к особому типу «any» и просто идет дальше.
Изначально мы планировали постепенно переносить файлы, по возможности просто расширяя стандартный код JavaScript за счет добавления более конкретных определений типов: добавление интерфейсов, определение методов классов как частных или общих методов, а также объявление перечислений. В процессе мы сделали два удивительных открытия:
Во-первых, нас удивило количество мелких ошибок, обнаруженных во время преобразования кода. Обсуждая этот момент с другими разработчиками, которые тоже начали использовать инструмент проверки типов, мы поняли, что это типичная ситуация: когда разработчик пишет очень много строк кода, в итоге практически неизбежно допускаются опечатки в свойстве, предполагается, что родительский объект для вложенного объекта существует во всех случаях, или используется нестандартный объект «error».
Во-вторых, мы недооценили возможности интеграции редактора. Благодаря языковой службе TypeScript редакторы, поддерживающие функцию автозаполнения, также поддерживают разработку на основе контекстно-зависимых вариантов. TypeScript понимает, какие свойства и методы доступны для отдельных объектов, и редактор, таким образом, получает ту же информацию. Система с функцией автозаполнения, которая использует только слова в текущем документе, после этого выглядит уже довольно примитивной. Прошли те времена, когда нам приходилось постоянно нырять в Google, чтобы проверить еще разок, какие события доступны в окне браузера Electron. Для Atom, Visual Studio Code, Sublime и практически любого другого редактора доступны подключаемые модули. Благодаря возможности проверить код, не закрывая редактор, производительность работы существенно повысилась.
![](https://habrastorage.org/files/68f/f54/e5c/68ff54e5c2ba40abbf010228f501b4ff.png)
В применении к обслуживанию кода перспективы экосистемы TypeScript выглядят весьма впечатляюще. Тем, кто активно использует экосистему React и Node/npm, доступность определений типов для сторонних библиотек дает огромные преимущества. Многие из импортируемых библиотек уже поддерживают TypeScript. Если определения не предоставляются вместе с модулем, скорее всего, их можно будет найти в проекте DefinitelyTyped. React, например, не предоставляет определений типов, в то время как простой пакет
Программа TypeScript стала настоящей находкой, обеспечивая стабильность в процессе работы и качественный результат, и в итоге через несколько дней с начала преобразования мы начали применять ее для всего нового кода. Аннотирование большей части кода JavaScript в базе кода классического приложения заняло примерно шесть месяцев.
Чтобы обеспечить понимание кода и возможность его обслуживания, весь размещенный код перед фиксацией автоматически проверяется с помощью TSLint. Таким образом, перед тем как Git зафиксирует внесенные изменения, сначала проверяется, что измененный код соответствует правилам проверки стиля оформления кода. Использование типа «any» явным образом не допускается: весь код приложения Slack должен явным образом указывать тип во всех случаях, когда TypeScript не может логически вывести его самостоятельно.
Когда необходимо создать ветвление, Git сначала исполняет всю базу кода в компиляторе TypeScript, который анализирует ее на предмет ошибок в структуре и функциях, а затем компилирует существующие функции (например, async/await) в код, совместимый с ES2016, на другом языке. К моменту создания запроса на включение внесенных изменений уже есть полная уверенность в том, что структурные зависимости в коде корректны.
Преимущества TypeScript однозначно перевешивают все реальные недостатки. Самым серьезным минусом в нашем случае можно считать дополнительные затраты на обучение. Разработчики, у которых есть опыт работы со строго типизированными языками, как правило, способны разобраться в синтаксисе в течение пары часов. Однако файл, в котором полноценно используются все функции TypeScript, может поставить в тупик тех, кто привык работать со стандартными кодами JavaScript.
Наиболее очевидным решением в данном случае было бы поэтапное представление функций. Можно просто включить TypeScript, не изменяя код, добавить несколько простых объявлений типов и сохранить более сложные схемы — наследование, универсальные шаблоны и расширенные типы (например, типы пересечений, сопоставленные типы) в отдельных модулях или на более позднем этапе. В конечном счете можно получить массу преимуществ, даже если используются только базовые функции TypeScript.
Slack ориентирован на технологии с открытым исходным кодом. И именно поэтому мы пытаемся сделать переход других разработчиков на TypeScript как можно более легким и комфортным. Все обнаруженные пробелы мы стремимся восполнить в кратчайшие сроки.
В частности, собственный компилятор Slack (компилятор Electron) позволяет разработчикам приложений Electron писать код в TypeScript, не беспокоясь о последующей компиляции. Библиотека RxJS, которая активно используется Slack, Nemutflix, GitHub и многими другими компаниями, при поддержке Slack теперь тоже перешла на TypeScript. Многие небольшие библиотеки, написанные нашими разработчиками классических приложений, медленно, но верно переходят к использованию TypeScript (например, spawn-rx, electron-spellchecker, electron-remote, electron-notification-state и electron-windows-notifications).
Перед началом работы с TypeScript ознакомьтесь с официальным руководством. Хороший практический пример малого порта можно посмотреть в описании порта spawn-rx. Отличный инструмент, который поможет успешно написать первые строки кода TypeScript для приложений Electron: electron-forge, где задействован компилятор Electron с поддержкой TypeScript. В нем есть даже удобный шаблон React/TypeScript с архитектурой, столь любимой нашей командой разработчиков приложений Slack. Если комбинирование веб-технологий с машинным кодом в целях создания кроссплатформенных классических приложений кажется вам интересной задачей, мы с нетерпением ждем вас!
Хочу также поделиться с вами полезным материалом: у нас вышла бесплатная электронная книга на русском языке для веб-разработчиков, в которой рассматриваются основные облачные службы и их выбор для решения конкретных задач. Книга содержит пошаговые руководства, примеры кода и приложений, а также данные бесплатной учетной записи.
Если вы увидели неточность перевода, сообщите, пожалуйста, об этом в личные сообщения. UPD: Спасибо IvaYan, что обратил внимание: перевод этой статьи был также опубликован ранее здесь.
![](https://habrastorage.org/files/3b5/444/efc/3b5444efccf746c49ed5622f48735684.jpg)
Когда Брендан Айх (Brendan Eich) создал самую первую версию JavaScript для Netscape Navigator 2.0 всего за десять дней, он, скорее всего, не ожидал, что его изобретение будет настолько применяться в классическом приложении Slack. Мы используем единую базу кода JavaScript для создания многопоточного классического приложения для ОС Windows, MacOS и Linux, которое осуществляет постоянное взаимодействие с машинным кодом.
Управление большими базами кода JavaScript представляется довольно сложной задачей. Каждый раз при передаче объектов из JavaScript в Chrome в Objective-C отдельные части кода должны гарантированно соответствовать друг другу для получения обратного вызова в другом потоке Node.js. В настольных операционных системах самая незначительная ошибка может привести к сбою приложения. Именно по этой причине мы внедрили TypeScript (расширенный набор кодов JavaScript на основе статических типов), после чего быстро научились не беспокоиться и доверять компиляторам. И мы не одиноки. По результатам опроса разработчиков StackOverflow в 2017 году TypeScript занял почетное третье место среди самых популярных технологий программирования. Учитывая, насколько быстро статическая проверка типов набирает популярность, нам хотелось бы поделиться своими знаниями и практическим опытом.
Статический анализ спешит на помощь
В прошлом мы применяли JSDoc для документирования подписей функций, а также использовали комментарии, чтобы сообщить разработчикам о цели и назначении классов, функций и переменных. В этом тоже есть свои минусы. С первого взгляда на код трудно понять, к чему приведет использование JavaScript. Приходится просто верить на слово, что разработчик, написавший этот код, правильно его задокументировал, и все те, кто впоследствии этот код изменял, должным образом отразили эти изменения в документации. В сложных системах, включающих множество модулей и зависимостей, можно запросто получить сбой в функции, даже не открывая файл, в котором она содержится.
Чтобы как-то улучшить ситуацию, мы решили попробовать применить статическую проверку типов. Инструмент статической проверки типов не изменяет поведение кода в среде выполнения. Вместо этого он анализирует код и пытается по возможности логически выводить типы, направляя разработчику предупреждение перед отсылкой кода. Инструмент статической проверки типов понимает, что
Math.random()
возвращает число, не содержащее строкового метода toLowerCase()
.![](https://habrastorage.org/files/574/71c/c78/57471cc7809d4e678105d825bf16f5ec.png)
Иными словами, при использовании такого инструмента обеспечивается поддержка системы за счет объявления типов вручную, что позволяет сообщать о предполагаемом поведении программы как пользователю, так и компьютеру. Приведенный ниже код определяет интерфейс для объекта «user» и метода, который предположительно должен получать данные о возрасте пользователя. Инструмент статической проверки типов анализирует этот код и предупреждает о стандартных ошибках (например, когда пользователь ожидает, что неопределенное свойство всегда будет доступно).
![](https://habrastorage.org/files/41b/d89/c5d/41bd89c5d7944c0d9a6589f8b313d8d5.png)
Что интересно, код не изменяется в среде выполнения: инструмент статической проверки типов не предоставляет служебных данных конечному пользователю. Приведенный выше пример в среде выполнения выглядит как совершенно стандартный код JavaScript:
![](https://habrastorage.org/files/30a/75e/d55/30a75ed557ab44039d29dc11e8a59f2e.png)
Интеллектуальный инструмент статической проверки типов позволяет быть уверенным в качестве кода, дает возможность выявлять типичные ошибки еще до того, как они совершены, а также обеспечивает автоматизированное документирование базы кода.
Перенос базы кода классического приложения Slack в TypeScript
Мы решили использовать программу TypeScript корпорации Microsoft, в которой функции статического анализа типов объединены с компилятором. Современный JavaScript — это действующий TypeScript, то есть TypeScript можно использовать, не меняя ни одной строчки кода. Таким образом, можно использовать «постепенную типизацию», чтобы выполнять компиляцию и статический анализ на ранних этапах и не прерывать работу на время устранения критических ошибок или добавления новых функций.
На практике включение функций анализа и компилятора без изменения кода означает, что TypeScript сразу же пытается проанализировать и расшифровать код. Программа использует встроенные типы и определения типов, доступные для сторонних зависимостей, чтобы проанализировать поток кода и выявить те мелкие ошибки, которые ранее остались незамеченными. Если программе TypeScript не удалось успешно расшифровать код, она относит его к особому типу «any» и просто идет дальше.
Изначально мы планировали постепенно переносить файлы, по возможности просто расширяя стандартный код JavaScript за счет добавления более конкретных определений типов: добавление интерфейсов, определение методов классов как частных или общих методов, а также объявление перечислений. В процессе мы сделали два удивительных открытия:
Во-первых, нас удивило количество мелких ошибок, обнаруженных во время преобразования кода. Обсуждая этот момент с другими разработчиками, которые тоже начали использовать инструмент проверки типов, мы поняли, что это типичная ситуация: когда разработчик пишет очень много строк кода, в итоге практически неизбежно допускаются опечатки в свойстве, предполагается, что родительский объект для вложенного объекта существует во всех случаях, или используется нестандартный объект «error».
Во-вторых, мы недооценили возможности интеграции редактора. Благодаря языковой службе TypeScript редакторы, поддерживающие функцию автозаполнения, также поддерживают разработку на основе контекстно-зависимых вариантов. TypeScript понимает, какие свойства и методы доступны для отдельных объектов, и редактор, таким образом, получает ту же информацию. Система с функцией автозаполнения, которая использует только слова в текущем документе, после этого выглядит уже довольно примитивной. Прошли те времена, когда нам приходилось постоянно нырять в Google, чтобы проверить еще разок, какие события доступны в окне браузера Electron. Для Atom, Visual Studio Code, Sublime и практически любого другого редактора доступны подключаемые модули. Благодаря возможности проверить код, не закрывая редактор, производительность работы существенно повысилась.
![](https://habrastorage.org/files/68f/f54/e5c/68ff54e5c2ba40abbf010228f501b4ff.png)
В применении к обслуживанию кода перспективы экосистемы TypeScript выглядят весьма впечатляюще. Тем, кто активно использует экосистему React и Node/npm, доступность определений типов для сторонних библиотек дает огромные преимущества. Многие из импортируемых библиотек уже поддерживают TypeScript. Если определения не предоставляются вместе с модулем, скорее всего, их можно будет найти в проекте DefinitelyTyped. React, например, не предоставляет определений типов, в то время как простой пакет
npm install @types/react
устанавливает такие определения по умолчанию, и последующая конфигурация уже не потребуется.Программа TypeScript стала настоящей находкой, обеспечивая стабильность в процессе работы и качественный результат, и в итоге через несколько дней с начала преобразования мы начали применять ее для всего нового кода. Аннотирование большей части кода JavaScript в базе кода классического приложения заняло примерно шесть месяцев.
Уверенная фиксация
Чтобы обеспечить понимание кода и возможность его обслуживания, весь размещенный код перед фиксацией автоматически проверяется с помощью TSLint. Таким образом, перед тем как Git зафиксирует внесенные изменения, сначала проверяется, что измененный код соответствует правилам проверки стиля оформления кода. Использование типа «any» явным образом не допускается: весь код приложения Slack должен явным образом указывать тип во всех случаях, когда TypeScript не может логически вывести его самостоятельно.
Когда необходимо создать ветвление, Git сначала исполняет всю базу кода в компиляторе TypeScript, который анализирует ее на предмет ошибок в структуре и функциях, а затем компилирует существующие функции (например, async/await) в код, совместимый с ES2016, на другом языке. К моменту создания запроса на включение внесенных изменений уже есть полная уверенность в том, что структурные зависимости в коде корректны.
Умение приходит не сразу
Преимущества TypeScript однозначно перевешивают все реальные недостатки. Самым серьезным минусом в нашем случае можно считать дополнительные затраты на обучение. Разработчики, у которых есть опыт работы со строго типизированными языками, как правило, способны разобраться в синтаксисе в течение пары часов. Однако файл, в котором полноценно используются все функции TypeScript, может поставить в тупик тех, кто привык работать со стандартными кодами JavaScript.
Наиболее очевидным решением в данном случае было бы поэтапное представление функций. Можно просто включить TypeScript, не изменяя код, добавить несколько простых объявлений типов и сохранить более сложные схемы — наследование, универсальные шаблоны и расширенные типы (например, типы пересечений, сопоставленные типы) в отдельных модулях или на более позднем этапе. В конечном счете можно получить массу преимуществ, даже если используются только базовые функции TypeScript.
Внести свой вклад
Slack ориентирован на технологии с открытым исходным кодом. И именно поэтому мы пытаемся сделать переход других разработчиков на TypeScript как можно более легким и комфортным. Все обнаруженные пробелы мы стремимся восполнить в кратчайшие сроки.
В частности, собственный компилятор Slack (компилятор Electron) позволяет разработчикам приложений Electron писать код в TypeScript, не беспокоясь о последующей компиляции. Библиотека RxJS, которая активно используется Slack, Nemutflix, GitHub и многими другими компаниями, при поддержке Slack теперь тоже перешла на TypeScript. Многие небольшие библиотеки, написанные нашими разработчиками классических приложений, медленно, но верно переходят к использованию TypeScript (например, spawn-rx, electron-spellchecker, electron-remote, electron-notification-state и electron-windows-notifications).
Перед началом работы с TypeScript ознакомьтесь с официальным руководством. Хороший практический пример малого порта можно посмотреть в описании порта spawn-rx. Отличный инструмент, который поможет успешно написать первые строки кода TypeScript для приложений Electron: electron-forge, где задействован компилятор Electron с поддержкой TypeScript. В нем есть даже удобный шаблон React/TypeScript с архитектурой, столь любимой нашей командой разработчиков приложений Slack. Если комбинирование веб-технологий с машинным кодом в целях создания кроссплатформенных классических приложений кажется вам интересной задачей, мы с нетерпением ждем вас!
Хочу также поделиться с вами полезным материалом: у нас вышла бесплатная электронная книга на русском языке для веб-разработчиков, в которой рассматриваются основные облачные службы и их выбор для решения конкретных задач. Книга содержит пошаговые руководства, примеры кода и приложений, а также данные бесплатной учетной записи.
Если вы увидели неточность перевода, сообщите, пожалуйста, об этом в личные сообщения. UPD: Спасибо IvaYan, что обратил внимание: перевод этой статьи был также опубликован ранее здесь.
Поделиться с друзьями
IvaYan
А чем это отличается от этой статьи?
Schvepsss
Формулировками)