TypeScript быстро
Қосымшада ыңғайлырақҚосымшаны жүктеуге арналған QRRuStore · Samsung Galaxy Store
Huawei AppGallery · Xiaomi GetApps

автордың кітабын онлайн тегін оқу  TypeScript быстро

 

Яков Файн, Антон Моисеев
TypeScript быстро
2021

Переводчик Д. Акуратер

Технический редактор М. Кольцов

Литературный редактор М. Петруненко

Художники Л. Егорова, В. Мостипан

Корректоры С. Беляева, Н. Викторова

Верстка Л. Егорова


 

Яков Файн, Антон Моисеев

TypeScript быстро . — СПб.: Питер, 2021.

 

ISBN 978-5-4461-1725-3

© ООО Издательство "Питер", 2021

 

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

 

Введение

Эта книга посвящена языку программирования TypeScript, который, по данным опроса разработчиков на Stack Overflow, является одним из популярнейших средств программирования (см. https://insights.stackoverflow.com/survey/2019). Как отмечает уважаемый ресурс ThoughtWork’s Technology Radar (http://mng.bz/Ze5P): «TypeScript — это тщательно проработанный язык, чьи постоянно совершенствующиеся инструменты и поддержка IDE не перестают удивлять нас. Обращаясь к отличному репозиторию определений типов TypeScript, мы используем преимущества всего богатства библиотек JavaScript, получая при этом еще и безо­пасность типов».

Мы пользуемся TypeScript ежедневно, и он нам очень нравится. Мы действительно ценим его за предоставляемую возможность фокусироваться на основной задаче, а не на опечатках в именах свойств объектов. В программах TypeScript шансы получения ошибок среды выполнения существенно ниже в сравнении с кодом, изначально написанным в JavaScript. Нам также нравится, что IDE предлагают отличную поддержку TypeScript и буквально проводят нас через весь лабиринт API из сторонних библиотек, которые используются в проектах.

TypeScript крут, но поскольку он является языком, компилируемым в JavaScript, необходимо немного поговорить о JS. В мае 1995 года через 10 дней усердной работы, Брендан Эйх (Brendan Eich) создал язык программирования JavaScript. Этот скриптовый язык не нуждался в компиляторе, и предполагалось, что он будет использоваться в браузере Netscape Navigator.

Для развертывания программы JavaScript в браузере не требовались компиляторы. Добавление тега <script> с исходным кодом JavaScript (или ссылкой на файл с исходным кодом) давало браузеру команду загружать и считывать код, выполняя его в браузерном движке JavaScript. Люди наслаждались простотой языка, в котором отсутствовала необходимость объявлять типы переменных и использовать какие-либо инструменты. Вы могли просто написать код в простом текстовом редакторе и затем использовать его на веб-странице.

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

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

На самом деле в JavaScript не существует способа объявить тип переменной. Движок этого языка угадывает тип на основе присвоенного значения (например, var x = 123 означает, что x является числом). Если позднее этот скрипт будет изменен на x = "678", то тип x изменится с числа на строку. Действительно ли вы хотели изменить тип x в данном случае или же это была ошибка? Об этом вы узнаете только во время выполнения, так как тут нет компилятора, который мог бы предупредить вас заранее.

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

Спустя годы JavaScript стал суперпопулярным и фактически стандартом языком веб-программирования. Однако 20 лет назад разработчики использовали его для отображения веб-страниц с небольшой долей интерактивного контента; сегодня же мы разрабатываем комплексные веб-приложения, содержащие тысячи строк кода, написанного командой разработчиков. Далеко не каждый в вашей команде будет помнить, что переменная x должна быть числом. Для минимизации числа ошибок среды выполнения JavaScript-разработчики создают юнит-тесты и производят код-ревью.

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

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

Благодарности

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

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

Отдельную благодарность выражаем всем рецензентам книги, предоставившим ценную обратную связь: Ахмаду Субахи (Ahmad Subahi), Александросу Далласу (Alexandros Dallas), Брайану Дэйли (Brian Daley), Кэмерону Пресли (Cameron Presley), Кэмерону Синге (Cameron Singe), Денизу Вехби (Deniz Vehbi), Флорис Бушо (Floris Bouchot), Джорджу Онофрею (George Onofrei), Джорджу Томасу (George Thomas), Джеральду Джеймсу Стралко (Gerald James Stralko), Гаю Лэнгстону (Guy Langston), Джеффу Смиту (Jeff Smith), Джастину Кану (Justin Kahn), Кенту Р. Спиллнеру (Kent R. Spillner), Кевину Орру (Kevin Orr), Лукасу Пардью (Lucas Pardue), Марко Летичу (Marco Letic), Маттео Баттиста (Matteo Battista), Полу Брауну (Paul Brown), Полине Кесельман (Polina Keselman), Ричарду Таттлу (Richard Tuttle), Шридхари Шридхарану (Sridhari Sridharan), Тамаре Форза (Tamara Forza) и Томасу Оверби Хансену (Thomas Overby Hansen).

О книге

Для кого эта книга

Эта книга написана для инженеров ПО, которые хотят повысить продуктивность разработки веб- или самостоятельных приложений. Мы, ее авторы, являемся практиками и книгу писали для таких же практиков. В ней не только объясняется синтаксис языка на простых примерах, но также описывается разработка нескольких приложений — так вы можете понять, как использовать TypeScript с популярными библиотеками и фреймворками.

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

Этот материал, в свою очередь, предполагает, что у читателей уже есть практические познания в HTML, CSS и JavaScript, использующем последние нововведения из спецификации ECMAScript. Если вам знаком только синтаксис ECMAScript 5, рекомендуем просмотреть приложение, чтобы лучше понимать примеры кода, используемые в книге. Приложение выполняет роль введения в современный JavaScript.

Структура книги

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

Глава 1 поможет начать разработку с помощью TypeScript. Вы скомпилируете и запустите самые простые программы, чтобы понять суть рабочего процесса — от написания программ в TypeScript и до их компиляции в выполняемый JavaScript. Мы также рассмотрим преимущества программирования в TypeScript в сравнении с JavaScript и представим вам редактор Visual Studio Code.

Глава 2 поясняет, как объявлять переменные и функции с типами. Вы научитесь объявлять псевдонимы типов с помощью ключевого слова type, а также узнаете, как объявлять пользовательские типы с классами и интерфейсами. Это поможет понять разницу между номинальными и структурными системами типов.

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

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

Глава 5 описывает декораторы, а также отображенные и условные типы. Все это относится к продвинутым типам TypeScript, поэтому для понимания этой главы следует ознакомиться с синтаксисом обобщенных типов.

Глава 6 посвящена сопутствующим инструментам. В ней мы рассказываем об использовании карт кода и TSLinter (несмотря на то, что TSLinter считается устаревшим, многие разработчики по-прежнему им пользуются). Затем мы покажем вам, как компилировать и обвязывать (bundle) приложения TypeScript с помощью Webpack. Вы также узнаете, как и зачем компилировать TypeScript с помощью Babel.

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

В части 2 мы используем TypeScript в блокчейн-приложении. Здесь вы можете подумать: «Ни одна из компаний, где я работал, не использует блокчейн, так зачем мне его изучать, если моя цель — освоение TypeScript?» Мы не хотели, чтобы наш образец приложения был очередным стандартным примером, поэтому озадачились поиском горячей технологии, в которой можно было бы применить различные элементы TypeScript, а также техники, представленные в части 1. Знакомство с тем, как TypeScript может быть использован в нетривиальном приложении, придаст содержанию больше практичности, даже если вы не собираетесь использовать блокчейн в ближайшем будущем.

В этой части книги вы разработаете несколько блокчейн-приложений: самостоятельное приложение, браузерное приложение, а также приложения на Angular, React.js и Vue.js. При этом можете выбрать к прочтению только интересующие вас главы, но обязательно прочитайте главы 8 и 10, где объясняются основные принципы.

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

В главе 9 описывается создание веб-клиента для блокчейна. Это приложение не будет использовать никакие фреймворки; в нем мы задействуем только HTML, CSS и TypeScript. Мы также создадим небольшую библиотеку для генерации хеша, которую можно будет использовать как в веб-, так и в самостоятельных клиентах. Помимо этого, вы увидите, как производить отладку кода TypeScript в своем браузере.

Глава 10 рассматривает код блокчейн-приложения, использующего сервер обмена сообщениями для связи между членами блокчейна. Мы создадим Node.js и WebSocket сервер в TypeScript и покажем вам, как для достижения консенсуса блокчейн использует правило длиннейшей цепочки. Вы найдете практические примеры использования TypeScript интерфейсов, абстрактных классов, квалификаторов доступа, перечислений и обобщенных типов.  

В главе 11 дается краткий обзор разработки веб-приложений в Angular при помощи TypeScript, а глава 12 рассматривает код блокчейн-веб-клиента, разработанного с помощью этого фреймворка.

Глава 13 дает краткое введение в разработку веб-приложений в React.js с помощью TypeScript, а глава 14 рассматривает код блокчейн-веб-клиента, разработанного с React.

Глава 15 аналогичным образом представляет разработку веб-приложений в Vue.js с помощью TypeScript, а глава 16 рассматривает блокчейн-веб-клиент, разработанный с использованием Vue.

О коде

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

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

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

Вторая часть состоит из нескольких проектов, где TypeScript используется для разработки приложений с помощью популярных библиотек и фреймворков (вроде Angular, React.js и Vue.js). Исходный код этих приложений размещен на GitHub по адресу https://github.com/yfain/getts.

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

Об авторах

Яков Файн является сооснователем двух IT-компаний: Farata Systems и SuranceBay. Он автор и соавтор таких книг, как Java Programming: 24-Hour Trainer, Angular Development with TypeScript1, Java Programming for Kids и др. Являясь чемпионом Java, провел множество классов и семинаров, посвященных веб- и Java-технологиям. Помимо этого, также был докладчиком на международных конференциях. Файн опубликовал более тысячи статей в своем блоге на yakovfain.com. В твиттере и инстаграме его можно найти по адресу @yfain. Он также публикует видео на YouTube.

Антон Моисеев является ведущим разработчиком в SuranceBay. Провел за разработкой корпоративных приложений более десяти лет, работая с Java и .NET. Имеет обширный опыт и фокусируется на развитии веб-технологий, реализующих лучшие методики слаженной работы фронтенда и бэкенда. Некоторое время проводил обучение по фреймворкам AngularJS и Angular. Периодически Антон делает посты в блоге antonmoissev.com. В твиттере вы можете найти его по адресу @antonmoiseev.

Об обложке

На обложке «TypeScript быстро» изображена «Буржуазная Флоренция». Эта иллюстрация позаимствована из коллекции костюмов различных стран, созданной Жаком Грассе де Сен-Совер (Jacques Grasset de Saint-Sauveur) (1757–1810) под названием «Costumes civils actuels de tuos les peoples connus», опубликованной во Франции в 1788 году. Каждая иллюстрация этой коллекции была нарисована и раскрашена от руки. Богатое разнообразие коллекции Грассе де Сен-Совер наглядно напоминает нам, насколько культурно разрознены были города и регионы всего мира еще 200 лет назад. Будучи изолированными друг от друга, люди говорили на различных диалектах и языках. На сельских же улицах было достаточно взглянуть на одеяние жителя, чтобы распознать, откуда он родом, кем работает и в каком жизненном положении находится.

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

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

1Файн Я., Моисеев А. Angular и TypeScript. Сайтостроение для профессионалов. — СПб.: Питер, 2018. — 464 с.: ил.

Файн Я., Моисеев А. Angular и TypeScript. Сайтостроение для профессионалов. — СПб.: Питер, 2018. — 464 с.: ил.

Яков Файн является сооснователем двух IT-компаний: Farata Systems и SuranceBay. Он автор и соавтор таких книг, как Java Programming: 24-Hour Trainer, Angular Development with TypeScript1, Java Programming for Kids и др. Являясь чемпионом Java, провел множество классов и семинаров, посвященных веб- и Java-технологиям. Помимо этого, также был докладчиком на международных конференциях. Файн опубликовал более тысячи статей в своем блоге на yakovfain.com. В твиттере и инстаграме его можно найти по адресу @yfain. Он также публикует видео на YouTube.

Часть 1. Основы синтаксиса TypeScript

Начнем часть 1 с объяснения преимуществ использования TypeScript в сравнении с JavaScript. После этого рассмотрим различные элементы синтаксиса TypeScript на примерах небольших фрагментов кода. Вы увидите, как использовать встроенные и объявлять пользовательские типы. Помимо этого, мы научимся применять классы, интерфейсы, а также обобщения, перечисления, декораторы, отображенные и условные типы. Вы познакомитесь с инструментами, которые используются разработчиками в TypeScript (например, компиляторы, линтеры, отладчики и бандлеры). В завершение мы покажем, как использовать в одном приложении код TypeScript совместно с JavaScript.

Для тех из вас, кто любит смотреть видео, Яков Файн опубликовал серию роликов на английском языке (см. http://mng.bz/m4M8), которые иллюстрируют содержимое части 1 этой книги. Если вы хотите как можно скорее освоить именно синтаксис TypeScript и его инструменты, часть 1 предоставит все, что для этого нужно.

1. Знакомство с TypeScript

В этой главе:

Преимущества программирования в TypeScript в сравнении с JavaScript.

Компиляция кода TypeScript в JavaScript.

Работа с редактором Visual Studio Code.

 

Цель этой главы — помочь вам начать использовать TypeScript для разработки. Начнем с выражения почтения самому JavaScript, а затем обоснуем, почему вам стоит программировать именно в TypeScript. В завершение главы скомпилируем и запустим очень простую программу, чтобы вы могли проследить рабочий процесс от написания программы в TypeScript до ее компиляции в исполняемый JavaScript вариант. Если вы уже опытный JS-разработчик, то нужна весомая причина для перехода на TypeScript, который в любом случае необходимо компилировать в JavaScript перед развертыванием. Если вы бэкенд-разработчик, планирующий изучать фронтенд экосистему, то вам также нужна причина для изучения языка, отличного от JavaScript. Что ж, давайте рассмотрим эти причины.

1.1. Зачем программировать в TypeScript

TypeScript — это язык, компилируемый в JavaScript. Он был выпущен корпорацией Microsoft в 2012 году как открытый проект. Программа, написанная в TypeScript, сперва должна быть транспилирована в JavaScript, и только потом ее можно выполнить в браузере или отдельном движке JavaScript.

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

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

TypeScript — это надмножество JavaScript, поэтому можно взять любой файл JavaScript вроде myProgram.js, изменить его расширение с .js на .ts, и получившийся файл myProgram.ts, скорее всего, станет рабочей программой TypeScript. Мы говорим «скорее всего», потому что оригинальный код JavaScript может иметь скрытые баги, связанные с типами (он может динамически изменять типы свойств объектов или добавлять новые после объявления объекта), а также другие проблемы, которые вскроются только после компиляции.

СОВЕТ В разделе 7.3 мы приведем несколько советов по миграции кода JavaScript в TypeScript.

В общем смысле слово надмножество подразумевает, что это надмножество содержит все, что есть в самом множестве, плюс что-то еще. Рисунок 1.1 представляет TypeScript как надмножество ECMAScript, являющегося спецификацией для всех версий JavaScript. ES.Next представляет самые последние дополнения в ECMAScript, которые еще дорабатываются.

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

 

Рис. 1.1. TypeScript как надмножество

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

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

let customerId: string;

customerId = 123; // ошибка при компиляции

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

let customerId = "A15BN"; // OK, customerId является строкой.

customerId = 123; // OK, с этого момента она является числом.

Теперь давайте рассмотрим JavaScript-функцию, которая применяет к цене скидку. У нее есть два аргумента, которые должны быть числами.

function getFinalPrice(price, discount) {

    return price - price / discount;

}

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

Это достаточно простая функция, но давайте представим, что кто-то вызвал ее, сообщив скидку в виде строки. В этом случае функция в среде выполнения выдаст ошибку NaN.

console.log(getFinalPrice( 100, "10%")); // выводит NaN

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

Официальная страница TypeScript (www.typescriptlang.org) предлагает документацию к этому языку и песочницу, где вы можете вводить фрагменты кода TypeScript, которые будут тут же скомпилированы в JavaScript.

По ссылке http://mng.bz/Q0Mm вы увидите наш фрагмент кода в упомянутой песочнице TypeScript. В коде есть красное волнистое подчеркивание под "10%". Если вы наведете указатель мыши на код с ошибкой, то увидите подсказку (рис. 1.2).

 

Рис. 1.2. Песочница TypeScript

Эта ошибка перехватывается статическим анализатором кода TypeScript во время вывода, еще до того, как будет произведена компиляция кода компилятором TypeScript (tsc). Более того, если вы укажете типы переменных, ваш редактор или IDE предложит автоподстановку имен аргументов и типов для функции getFinalPrice().

Разве не прекрасно, что ошибки перехватываются до выполнения программы? Мы убеждены, что да. Многие разработчики, имеющие опыт работы в  Java, C++ и C#, воспринимают такое обнаружение ошибок как само собой разу­меющееся, и именно эта особенность является одной из основных причин выбора TypeScript.

ПРИМЕЧАНИЕ Есть два вида программных ошибок: те, о которых сообщают вспомогательные инструменты в процессе набора кода, и те, о которых сообщают пользователи вашей программы. Программирование в TypeScript существенно снижает количество ошибок второго типа.

СОВЕТ На сайте TypeScript (www.typescriptlang.org) есть раздел «Documentation and tutorials» (Документация и обучающий материал). Там вы найдете полезные советы по настройке TypeScript в различных средах вроде ASP.NET, React и пр.

Некоторые матерые JS-разработчики говорят, что TypeScript только замедляет рабочий процесс, требуя объявлений типов, и что в JavaScript их продуктивность выше. Но при этом стоит помнить, что типы в TypeScript используются по желанию, то есть вы можете продолжить писать на JavaScript, но с участием tsc в рабочем процессе. Зачем? Потому, что вы сможете использовать новейший синтаксис ECMAScript (вроде async и await), а затем транспилировать ваш JavaScript код в ES5, чтобы он мог быть запущен в более старых браузерах.

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

Более сотни языков программирования компилируются в JavaScript (согласно списку: http://mng.bz.MO42). TypeScript же на их фоне выделяется тем, что его создатели следуют стандартам ECMAScript и реализуют намечающиеся функции JavaScript намного быстрее, чем поставщики браузеров.

Вы можете найти текущие предложения по введению новых возможностей ECMAScript на GitHub: https://github.com/tc39/proposals. Предложение должно пройти несколько стадий, чтобы быть включенным в итоговую версию следующей спецификации ECMAScript. Если предложение доходит до третьей стадии, то, скорее всего, будет включено в последнюю версию TypeScript.

Летом 2017 года в спецификацию ECMAScript ES2017 (ES8) были введены ключевые слова async и await. Ведущим браузерам при этом потребовалось более года, чтобы внедрить их поддержку, TypeScript же начал поддерживать их с ноября 2015 года. TypeScript-разработчики получили возможность использовать эти ключевые слова на три года раньше тех, кто ожидал появления их поддержки в браузерах. Самое лучшее в этом то, что вы можете использовать будущий синтаксис JavaScript в сегодняшнем коде TypeScript и транспилировать его в более старые версии синтаксиса JavaScript (вроде ES5), которые поддерживаются всеми браузерами.

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

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

TypeScript следует новейшим спецификациям ECMAScript и добавляет к ним типы, интерфейсы, декораторы, переменные членов классов (поля), обобщенные типы, перечисления, ключевые слова public, protected, private и другие возможности. Изучите дорожную карту TypeScript (https://github.com/Microsoft/TypeScript/wiki/Roadmap), чтобы посмотреть доступные и ожидаемые возможности. И еще одно: JavaScript-код, сгенерированный из TypeScript, легко читается и выглядит как набранный вручную.

Пять фактов о TypeScript

Главным разработчиком TypeScript является Андерс Хейлсберг, который также спроектировал Turbo Pascal, Delphi и был ведущим проектировщиком C# в Microsoft.

В конце 2014 года корпорация Google обратилась к Microsoft с просьбой ввести в TypeScript декораторы, чтобы этот язык мог использоваться для разработки на фреймворке Angular 2. В Microsoft согласились, и это чрезвычайно повысило популярность TypeScript, учитывая, что сотни тысяч разработчиков используют Angular.

По состоянию на декабрь 2019 года tsc имел несколько миллионов скачиваний в неделю с сайта npmjs.org, а это не единственный репозиторий TypeScript. Текущую статистику можете просмотреть по этому адресу: www.npmjs.com/package/typescript.

Согласно Redmonk, авторитетной компании по аналитике ПО, TypeScript занял 12-е место в рейтинге языков программирования в январе 2019 года (рейтинг можно посмотреть здесь: http://mng.bz/4eow).

Согласно опросу разработчиков, проведенному Stack Overflow в 2019 году, TypeScript занимает третье место в списке любимых языков (https://insights.stackoverflow.com/survey/2019).

 

Далее мы представим процесс настройки использования tsc на вашем компьютере.

1.2. Типичные рабочие процессы TypeScript

Давайте познакомимся с рабочим процессом TypeScript, начиная с написания кода и заканчивая развертыванием приложения. На рис. 1.3. представлен этот процесс. Весь исходный код написан в TypeScript.

 

Рис. 1.3. Развертывание приложения, написанного в TypeScript

Как вы можете видеть, проект состоит из трех файлов TypeScript: a.ts, b.ts и c.ts. Эти файлы должны быть скомпилированы в JavaScript компилятором TypeScript (tsc), который сгенерирует три новых файла: a.js, b.js и c.js. Чуть позже мы покажем, как настроить компилятор на генерацию JavaScript конкретных версий.

Сейчас некоторые JavaScript-разработчики скажут: «TypeScript вынуждает меня вводить дополнительный шаг компиляции между написанием и выполнением кода». Но вопрос в том, действительно ли вы хотите придерживаться версии ES5 JavaScript, игнорируя весь новейший синтаксис, добавленный в ES6, 7, 8 вплоть до ES.Next? Если нет, то в любом случае будете использовать шаг компиляции в своем рабочем процессе, так как потребуется компилировать исходный код, написанный в более новой версии JavaScript, в широко поддерживаемый синтаксис ES5.

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

JavaScript-разработчики используют различные бандлеры вроде Webpack или Rollup, которые не только конкатенируют несколько файлов JavaScript, но также могут оптимизировать код и удалить его неиспользуемые части (выполняя перетряхивание дерева). Если ваше приложение состоит из нескольких модулей, каждый из них может быть развернут как отдельная связка.

На рис. 1.3 изображена всего одна развернутая связка — main.js. Если бы это было веб-приложение, то в нем бы был HTML-файл с тегом <script src='main.js'>. Если бы приложение запускалось в самостоятельном JavaScript-движке вроде Node.js, то вы могли бы начать со следующей команды (предполагается, что Node.js установлен):

node main.js

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

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

Рисунок 1.4 показывает пример рабочего процесса для приложения, использующего популярную JavaScript-библиотеку lodash.

 

Рис. 1.4. Развертывание приложения, написанного совместно в TypeScript и JavaScript

Эта диаграмма включает файл определений типов lodash.d.ts, который используется tsc в процессе разработки. Он также включает актуальную библиотеку lodash.js, которая будет связана с остальной частью приложения во время его развертывания. Термин «связка» относится к процессу объединения нескольких файлов сценариев (скриптов) в один.

1.3. Использование компилятора TypeScript

Пришло время изучить компиляцию простой программы TypeScript в ее JavaScript-версию. Компилятор (tsc) может быть связан с выбранной вами IDE или установлен как ее плагин. Мы же предпочитаем устанавливать его независимо от IDE, используя пакетный менеджер npm, поставляемый с Node.js.  Node.js (или просто Node), — это не просто фреймворк или библиотека, но также и среда выполнения JavaScript. Мы используем ее для выполнения различных утилит вроде npm или запуска JavaScript-кода без браузера.

Для начала вам потребуется скачать и установить текущую версию Node.js с https://nodejs.org. Он установит node и npm.

С помощью npm вы можете устанавливать ПО либо локально в директорию вашего проекта, либо глобально, чтобы можно было использовать его в разных проектах. Мы будем использовать npm, чтобы установить tsc и другие пакеты из репозитория, размещенного на www.npmjs.com и содержащего более полумиллиона различных пакетов.

Вы можете установить tsc глобально (используя опцию -g), выполнив следующую команду в окне терминала:

npm install -g typescript

ПРИМЕЧАНИЕ Для простоты в первой части книги мы будем использовать tsc, установленный глобально. Тем не менее в реальных проектах мы предпочитаем устанавливать tsc локально в директорию проекта посредством его добавления в раздел devDependencies файла package.json. Вы увидите, как это делается, в главе 8, когда мы начнем работать над примером блокчейн-проекта.

В примерах кода этой книги мы использовали TypeScript 3-й версии или новее. Чтобы проверить версию вашего tsc, выполните следующую команду в терминале:

tsc -v

Теперь давайте посмотрим, как скомпилировать простую программу из TypeScript в JavaScript. Выберите любую директорию и создайте в ней новый файл main.ts со следующим содержимым.

Листинг 1.1. Файл main.ts

 

Следующая команда скомпилирует main.ts в main.js:

tsc main

Она вызовет появление сообщения об ошибке «аргумент типа '10%' не может быть присвоен параметру типа 'number'», но при этом все равно сгенерирует файл main.js со следующим содержимым:

Листинг 1.2. Получившийся файл main.js

 

Здесь вы можете спросить: «Какой смысл создания JavaScript-файла при наличии ошибки компиляции?» Что ж, с точки зрения Javascript содержимое файла main.js допустимо. Но в реальных TypeScript-проектах мы не хотим допускать генерацию кода для файлов с ошибками.

Для этого есть решение. Компилятор tsc предлагает десятки опций компиляции, описанных в документации TypeScript (http://mng.bz/rf14), одной из которых является noEmitOnError. Удалите файл main.js и попробуйте скомпилировать main.ts следующим образом:

tsc main --noEmitOnError true

В этот раз файл main.js сгенерирован не будет, пока мы не устраним ошибку в main.ts.

СРЕДА REPL ДЛЯ TYPESCRIPT

REPL расшифровывается как Real-Evaluate-Print-Loop (цикл чтение-вычисление-печать) и относится к простой интерактивной языковой оболочке, позволяющей быстро выполнять фрагменты кода. Интерактивная среда TS по адресу www.typescriptlang.org/play представляет собой вариант REPL, в котором вы можете писать, компилировать и выполнять фрагменты кода, находясь в браузере. Следующий пример показывает, как можно использовать песочницу для компиляции простого TS класса в версию ES5 JavaScript.

 

Транспиляция TypeScript в ES5

На следующем рисунке представлена компиляция того же кода в версию ES6.

 

Транспиляция TypeScript в ES6

В песочнице есть меню Options, где можно выбирать опции компилятора. В частности, можно выбрать целевую версию для компиляции, например ES2018 или ES5.

Если хотите запускать фрагменты кода из командной строки без участия браузера, установите TypeScript Node REPL, доступный по ссылке https://github.com/TypeStrong/ts-node.

СОВЕТ Включение опции noEmitOnError означает, что ранее сгенерированный файл не будет замещен, пока все ошибки в файлах TypeScript не будут устранены.

Опция компилятора --t позволяет устанавливать синтаксис целевого JavaScript-файла. Например, вы можете использовать один и тот же исходный файл и генерировать его в одноранговую версию JavaScript с синтаксисом ES5, ES6 или более новым. Так можно скомпилировать код в синтаксис ES5:

tsc --t ES5 main

Tsc позволяет вам предварительно настроить процесс компиляции (устанавливая исходную директорию и директорию назначения, целевую версию и т.д.). Если ваш файл tsconfig.json расположен в директории проекта, вы можете просто ввести tsc в командной строке, и компилятор считает из него все настройки. Пример файла tsconfig.json показан ниже.

Листинг 1.3. Файл tsconfig.json

 

СОВЕТ Опция компилятора target также используется для проверки синтаксиса. Например, если вы укажете целевую версию для компиляции как es3, TypeScript будет ругаться на методы-геттеры в вашем коде. Так произойдет потому, что он не знает, как компилировать геттеры в версию ECMAScript 3.

Давайте посмотрим, сможете ли вы проделать все описанное, следуя инструкциям:

1. Создайте файл tsconfig.json в каталоге, где расположен файл main.ts, и добавьте в него следующее:

{

    "compilerOptions": {

        "noEmitOnError": true,

        "target": "es5",

        "watch": true

    }

}

Обратите внимание на последнюю опцию — watch. Благодаря ей компилятор будет следить за вашими файлами TypeScript, и если они изменятся, tsc их перекомпилирует.

2. В терминале проследуйте до директории, где расположен файл tsconfig.json, и выполните следующую команду:

tsc

Вы увидите сообщение об ошибке, уже описанное в этом разделе ранее, но компилятор не совершит выход, так как запущен в режиме наблюдения. При этом файл main.js создан не будет.

3. Исправьте ошибку, и код будет автоматически перекомпилирован. Посмотрите и убедитесь, что на этот раз файл main.js будет создан.

Если вы хотите выйти из режима наблюдения, просто нажмите Ctrl+С на клавиатуре, находясь в окне терминала.

СОВЕТ Для начала нового проекта TypeScript запустите команду tsc --init в любой директории. Она создаст за вас файл tsconfig.json, содержащий все опции компилятора, большинство из которых будут закомментированы. Раскомментируйте их при необходимости.

ПРИМЕЧАНИЕ Файл tsconfig.json может наследовать конфигурацию из другого файла с помощью свойства extends. В главе 10 мы рассмотрим пример проекта, использующего три файла конфигурации: первый с обычными опциями компилятора tsc для всего проекта, второй для клиента и третий для серверной части проекта. Подробнее читайте в разделе 10.4.1.

1.4. Знакомство с Visual Studio Code

Интегрированные среды разработки (IDE) и редакторы кода повышают производительность разработчиков, при этом TypeScript отлично поддерживается следующими инструментами: Visual Studio Code, Web-Storm, Eclipse, Sublime Text, Atom, Emacs, Vim. Здесь мы решили использовать открытый и бесплатный редактор Visual Studio Code (VS Code), разработанный Microsoft. Вы же для работы с TypeScript можете использовать любой другой редактор или IDE.

ПРИМЕЧАНИЕ Согласно опросу разработчиков на Stack Overflow (https://insights.stackoverflow.com/survey/2019), VS Code является самой популярной средой разработки, которую используют более 50% пользователей. Кстати, этот редактор также написан на TypeScript.

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

Можно скачать VS Code с https://code.visualstudio.com. Процесс установки будет зависеть от ОС вашего компьютера. Подробное описание этого процесса см. в разделе Setup документации VS Code (https://code.visualstudio.com/docs).

После установки запустите программу. Затем, используя опцию меню File>Open, откройте директорию chapter1/vscode, которая прилагается к примерам кода этой книги. В ней содержится файл main.js из предыдущего раздела и простой файл tsconfig.json. На рис. 1.5 аргумент "10%" подчеркнут, что указывает на ошибку. Если вы наведете указатель мыши на подчеркнутый код, то будет показано то же сообщение об ошибке, которое мы видели на рис. 1.2.

 

Рис. 1.5. Подчеркивание ошибок в VS Code

Режимы VS Code для TypeScript

VS Code поддерживает два режима для TypeScript кода: область файла и явный проект. Режим области файла ограничен, так как не позволяет скрипту файла использовать переменные, объявленные в других файлах. Режим же явного проекта требует, чтобы файл tsconfig.json находился в директории проекта.

Файл tsconfig.json, относящийся к текущему разделу, прилагается.

Листинг 1.4. vscode/tsconfig.json

 

 

Если вы хотите открывать VS Code из командной строки, то его исполняемый файл нужно будет добавить в переменную окружения PATH вашего компьютера. В Windows процесс настройки должен сделать это автоматически. В MacOS запустите VS Code, выберите опцию меню View > Command Palette, напечатайте shell command и выберите эту опцию: Shell Command: Install ‘code’ Command in PATH. Затем перезапустите терминал и введите code из любой директории. Запустится VS Code, и вы сможете работать с файлами из директории, в которой находитесь.

В предыдущем разделе мы компилировали код в отдельном окне терминала, но VS Code имеет встроенный терминал. Благодаря этому исчезает необходимость покидать окно редактора для использования окна командной строки. Чтобы открыть терминал VS Code, выберите в меню View > Terminal или Terminal > New Terminal.

На рис. 1.6 показано, как выглядит интегрированный терминал после выполнения команды tsc. Стрелочка справа указывает на значок «плюс», который позволяет открывать любое количество окон терминалов. Мы закомментировали последнюю строку, где есть ошибка, и tsc сгенерирует файл main.js в директорию dist.

 

Рис. 1.6. Выполнение команды tsc в VS Code

СОВЕТ VS Code выбирает компилятор tsc, использующийся в Node.JS у вас на компьютере. Откройте любой файл TypeScript, и вы увидите версию tsc, указанную на нижней панели инструментов справа. Если предпочитаете использовать tsc, который установили глобально, щелкните по номеру версии в нижнем правом углу и выберите нужный вам tsc-компилятор.

На рис. 1.6 в нижней части черной панели слева вы можете увидеть квадратную иконку, которая используется для поиска и установки расширений с маркетплейса VS Code. Такие расширения могут сделать программирование в TypeScript более гибким:

• ESLint — интегрирует линтер JavaScript и проверяет код на читаемость и обслуживаемость.

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

• Path Intellisense — автоматически подставляет пути файлов.

Более подробно об использовании VS Code для TypeScript вы можете прочитать в документации продукта: https://code.visualstudio.com/docs/languages/typescript.

СОВЕТ Есть прекрасная онлайн-IDE под названием StackBlitz (https://stackblitz.com). Основана она на технологии VS Code и не требует установки на компьютер.

ПРИМЕЧАНИЕ Часть 2 книги содержит различные версии примера блокчейн-приложения. Несмотря на то что часть 2 необязательна к прочтению, рекомендуем ознакомиться хотя бы с главами 8 и 9.

Итоги

• TypeScript — это надмножество JavaScript. Программа, написанная на TypeScript, должна быть сначала транспилирована в JavaScript, а только затем может быть выполнена в браузере или отдельном движке JavaScript.

• Ошибки перехватываются статическим анализатором кода TypeScript во время набора еще до компиляции кода компилятором tsc.

• TypeScript дает преимущества статически типизированных языков везде, где это нужно, не препятствуя при этом использованию старых добрых динамических объектов JavaScript.

• TypeScript следует новейшим спецификациям стандарта ECMAScript и добавляет к ним типы, интерфейсы, декораторы, переменные членов классов (поля), обобщенные типы, перечисления, ключевые слова public, protected, private и другие возможности. Ознакомьтесь с дорожной картой TypeScript здесь: https://github.com/Microsoft/TypeScript/wiki/Roadmap, чтобы узнать о текущих и будущих возможностях.

• Чтобы начать новый проект TypeScript, выполните команду tsc --init в любой директории. Она создаст за вас файл tsconfig.json, который будет содержать все опции компилятора, большинство из которых будут закомментированы. Раскомментируйте их по необходимости.

2. Базовые и пользовательские типы

В этой главе:

Объявление переменных с типами и использование типов в объявлениях функций.

Объявление псевдонимов типов с помощью ключевого слова type.

Объявление пользовательских типов с помощью классов и интерфейсов.

 

Можно рассматривать TypeScript как JavaScript с типами, хотя это будет очень упрощенное представление, так как в TypeScript имеются элементы синтаксиса, отсутствующие в JavaScript (например, интерфейсы, обобщенные типы и др.). Тем не менее основная сила TypeScript заключается именно в типах.

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

ПРИМЕЧАНИЕ Если вы не знакомы с синтаксисом современного JavaScript, то рекомендуем сначала прочитать приложение, а затем уже продолжать освоение TypeScript. Это приложение также поможет понять, какие элементы синтаксиса существуют в JavaScript, а какие были добавлены именно в TypeScript.

2.1. Объявление переменных с типами

Зачем объявлять типы переменных, если в JavaScript вы можете просто объявить имя переменной и хранить в ней данные любого типа? Написание кода в JS легче, чем в других языках, в основном потому, что вам не нужно указывать типы для идентификаторов, не так ли?

Более того, в JavaScript вы можете присваивать переменной сперва численное значение, а позднее текстовое. Так не выйдет в TypeScript, где, присвоив переменной тип, вы уже не сможете его изменить. Рисунок 2.1 показывает это на примере:

 

Рис. 2.1. Попытка изменить тип переменной в TypeScript (слева) и JavaScript (справа)

В левой части рис. 2.1 вы видите код TypeScript, написанный в интерактивной среде www.typescriptlang.org. Но где же мы объявили тип переменной taxCode? Мы не делали этого явно, но поскольку инициализировали ее с численным значением, TypeScript присвоил taxCode тип number.

Вторая строка подчеркнута волнистой линией, указывающей на ошибку. Если вы наведете курсор на это подчеркивание, то сообщение об ошибке укажет, что  'lowincome' не может быть присвоено типу 'number'. В мире TypeScript это означает, что если вы объявили переменную как хранящую численные значения, то уже не можете присваивать ей строчные. В правой части рис. 2.1 скомпилированный код JavaScript не показывает ошибки, так как JavaScript во время выполнения позволяет присваивать переменной значения различных типов.

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

Тип может быть присвоен переменной явно самим разработчиком либо неявно (вывод типа) компилятором TypeScript. На рис. 2.1 мы объявили переменную taxCode, не присваивая ей тип. Присваивание значения 1 этой переменной позволяет компилятору понять, что ее тип — number. Это пример вывода типа. Большинство примеров кода в следующем разделе используют явные типы, за парой исключений, помеченных как выведенные.

2.1.1. Базовые аннотации типов

Когда вы объявляете переменную, то можете добавить двоеточие и аннотацию типа, чтобы указать тип этой переменной:

let firstName: string;

let age: number;

TypeScript предлагает следующие аннотации типов:

• string — для текстовых данных;

• boolean — для значений true/false;

• number — для численных значений;

• symbol — уникальное значение, создаваемое вызовом конструктора Symbol;

• any — для переменных, способных содержать значения разных типов, которые могут быть вам неизвестны в момент написания кода;

• unknown — аналог any, с которым нельзя производить никаких действий, не утвердив или сузив его до более конкретного типа;

• never — для представления невозможного кода (вскоре будет приведен пример);

• void — отсутствие значения.

Большинство базовых типов самоописательны и не требуют дополнительных пояснений. Отдельно стоит отметить, что в ECMAScript 2015 был добавлен примитивный тип данных symbol, который всегда уникален и неизменен. Например, в следующем фрагменте кода sym1 не равен sym2:

const sym1 = Symbol("orderID");

const sym2 = Symbol("orderID");

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

Листинг 2.1. Символы как свойства объектов

const ord = Symbol('orderID');  

  Создает новый символ

 

const myOrder = {  

  Использует символ как свойство объекта

    ord: "123"

 

};

console.log (myOrder['ord']);   

  Эта строка выводит «123»

Будучи надмножеством JavaScript, TypeScript также имеет два специальных значения: null и undefined. Переменная, которой не было присвоено значение, имеет значение undefined. Функция, не возвращающая значение, также имеет значение undefined. Значение null представляет намеренное отсутствие значения, как в let someVar =null;.

Вы можете присвоить значения null и undefined переменным любого типа, но чаще всего они используются в сочетании со значениями других типов. Следующий фрагмент кода показывает, как вы можете объявить функцию, возвращающую либо string, либо null (вертикальная черта представляет собой объединенный тип, который рассматривается в разделе 2.1.3):

function getName(): string | null {

    ...

}

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

Если вы объявляете переменную типа any, то можете присвоить ей любое значение: численное, текстовое, логическое или пользовательское вроде Customer. Тем не менее следует избегать использования any, так как иначе вы теряете все преимущества проверки типов и читаемость вашего кода также падает.

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

Листинг 2.2. Стрелочная функция, возвращающая тип never

const logger = () => {

    while (true) {  

  Эта функция никогда не завершается

        console.log("The server is up and running");

    }

};

В приведенном листинге logger получает тип () => never. В листинге 2.9 вы увидите еще один пример присваивания типа never.

Тип void не используется при объявлении переменных, а применяется для объявления функции, не возвращающей значение:

function logError(errorMessage: string): void {

    console.error(errorMessage);

}

В отличие от типа never, функция void завершает свое выполнение, но не возвращает значение.

СОВЕТ Если тело функции не имеет инструкции return, то она все равно воз­вращает значение undefined. Аннотация типа void может использоваться для предотвращения случайного возвращения программистами явного значения из функции.

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

 

Первая строка объявляет и инициализирует переменную name1 в стиле JavaScript, и мы можем сказать, что для нее выводится тип string. Считаете ли вы, что вторая строка — это удачный пример объявления и инициализации переменной name2 в TypeScript? Скажем так, с точки зрения стиля кода указание типа здесь излишне.

Хоть вторая строка и является корректным синтаксисом TypeScript, указывать тип string в ней не обязательно, поскольку переменная инициализирована со строкой и TypeScript выведет тип name2 как string.

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

Листинг 2.3. Идентификаторы с выведенными типами

const age = 25;  

  Константа age не объявляет свой тип

 

function getTax(income: number): number {

    return income * 0.15;

}

 

let yourTax = getTax(50000);  

  Переменная yourTax не объявляет свой тип

TypeScript также позволяет использовать в качестве типов литералы. Следующая строка объявляет переменную типаJohnSmith.

let name3: 'John Smith';

Мы можем сказать, что переменная name3 имеет литеральный тип JohnSmith и допустит только одно значение — JohnSmith. Любая попытка присвоить другое значение переменной литерального типа будет обозначена модулем проверки типов как ошибка:

let name3: 'John Smith';

 

name3 = 'Mary Lou'; // ошибка: Тип '"Mary Lou"' не может быть присвоен типу

Ä '"John Smith"'

Маловероятно, что вы будете использовать строчные литералы для объявления типа, как в случае с переменной name3. Но можно  использовать строчные литералы в объединениях (объясненных в разделе 2.1.3) и перечислениях (рассмотренных в главе 4).

Вот несколько примеров переменных, объявленных с явными типами:

let salary: number;

let isValid: boolean;

let customerName: string = null;

Расширение типов

Если вы объявляете переменную, не инициализируя ее с конкретным значением, TypeScript использует внутренние типы null или undefined, которые преобразуются в any. Это называется расширением типов.

Переменная принимает значение undefined:

let productId;

productId = null;

productId = undefined;

Компилятор TypeScript применяет расширение типов и присваивает тип any значениям null и undefined. Поэтому тип переменной productId — это any.

Стоит упомянуть, что компилятор TypeScript поддерживает опцию --strictNullCheck, запрещающую присвоение null переменным с известными типами. В следующем фрагменте кода productId имеет тип number, а вторая и третья строки не будут скомпилированы, если включить --strictnullCheck:

let productId = 123;

productId = null; // ошибка компилятора

productId = undefined; // ошибка компилятора

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

СОВЕТ Добавляйте явные аннотации типов для сигнатур функций или методов, а также членов публичных классов.

ПРИМЕЧАНИЕ TypeScript включает и другие типы, используемые при взаимодействии с браузером, вроде HTMLElement и Document. Помимо этого, вы также можете использовать ключевые слова type, class и interface, чтобы объявлять собственные типы вроде Customer или Person. В следующем разделе мы покажем, как это делать. Там же вы узнаете, как можно комбинировать типы с помощью объединений.

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

2.1.2. Типы в объявлениях функций

Функции и функциональные выражения в TypeScript похожи на функции JavaScript, но позволяют вам явно объявлять типы их аргументов и возвращаемых значений.

Давайте начнем с написания JavaScript-функции (без аннотации типов), которая вычисляет налог. Функция в следующем листинге имеет три параметра и будет производить вычисление на основе штата проживания, дохода и количества иждивенцев. За каждого иждивенца гражданин имеет право на налоговый вычет в размере от 300 до 500 долларов, в зависимости от штата проживания.

Листинг 2.4. Вычисление налога в JavaScript

 

Предположим, что гражданин с доходом 50 000 долларов проживает в штате Нью-Джерси и имеет двух иждивенцев. Давайте вызовем calcTax():

let tax = calcTax('NJ', 50000, 2);

Переменная tax получает значение 1,900, являющееся верным. Даже несмотря на то что calcTax() не объявляла типы для параметров функции, мы догадались, как вызвать эту функцию, исходя из имен параметров.

А если бы мы ошиблись в догадке? Давайте вызовем ее неверным способом, передав в качестве числа иждивенцев значение string:

let tax = calcTax('NJ', 50000, 'two');

Вы не узнаете о проблеме, пока не вызовете эту функцию. Переменная tax будет иметь значение NaN (не число). Баг прокрался только потому, что вы не смогли явно указать типы параметров, а компилятор не смог вывести типы аргументов функции.

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

Листинг 2.5. Вычисление налога в TypeScript

 

Теперь уже не выйдет допустить ту же ошибку и передать для числа иждивенцев значение string:

let tax: number = calcTax('NJ', 50000, 'two');

Компилятор TypeScript отобразит ошибку: «Аргумент типа string не может быть присвоен параметру типа number». Более того, возвращаемое значение функции объявлено как number, что мешает вам допустить другую ошибку и присвоить результат вычисления налога не численной переменной:

let tax: string = calcTax('NJ', 50000, 'two');

Исправление функции calcTax()

В этом разделе приведены TypeScript и JavaScript версии функции calcTax(), но они обрабатывают только два штата: Нью-Йорк и Нью-Джерси. Вызов этих функций для любого другого штата вернет undefined в среде выполнения.

Компилятор TypeScript не предупредит вас о том, что функция в листинге 2.5 написана плохо и может вернуть undefined, но синтаксис TypeScript позволяет вам предупредить читающего код человека, что функция в листинге 2.5 может вернуть не только число, но и значение undefined, если будет вызвана со штатом, отличным от Нью-Йорка или Нью-Джерси. Для объявления подобного случая использования вам следует изменить сигнатуру функции так:

function calcTax(state: string, income: number, dependents: number) :

Ä number | undefined

 

Компилятор это перехватит и выдаст ошибку «Тип ‘number’ не может быть присвоен типу ‘string’: var tax: string». Такая проверка типов в процессе компиляции может сэкономить вам уйму времени работы над любым проектом.

2.1.3. Объединенный тип

Объединения позволяют вам выражать значение, которое может состоять из нескольких типов. Вы можете объявлять пользовательский тип, основанный на двух или более существующих типах. Например, вы можете объявить переменную типа, который может принимать либо значение string, либо значение number (вертикальная черта означает объединение):

let padding: string | number;

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

TypeScript поддерживает тип any, но предыдущий вариант объявления имеет преимущества в сравнении с объявлением letpadding:any. Листинг 2.6 показывает один образец кода из документации TypeScript (см. http://mng.bz/5742). Эта функция может добавить левый отступ для предоставленной строки. Отступ может быть определен либо как строка, предшествующая указанному значению, либо как число пробелов, которые должны предшествовать указанной строке.

Листинг 2.6. padLeft с типом any

 

Следующий листинг демонстрирует использование padLeft():

Листинг 2.7. Вызов функции padLeft

 

Защиты типа typeof и instanceof

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

Схожим образом защита типа instanceof используется с пользовательскими типами (конструкторами), о чем подробнее рассказано в разделе 2.2. Защита instanceof позволяет проверить действительный тип при выполнении:

if (person instanceof Person) {...}

Различие между typeof и instanceof состоит в том, что первая используется со встроенными типами, а последняя — с пользовательскими.

СОВЕТ В разделе 2.2.4 мы объясним структурную систему типов, реализованную в TypeScript. Коротко говоря, объект, созданный с помощью синтаксиса объектного литерала (синтаксис с фигурными скобками), может быть использован там, где ожидается объект класса (вроде Person), если объектный литерал имеет те же свойства, что и Person. Из-за этого if (person instanceof Person) может дать вам ложное отрицание, если переменная person будет указывать на объект, созданный конструктором класса Person.

Если теперь мы изменим тип padding на объединение string и number (как показано в следующем листинге), то компилятор сообщит об ошибке, если вы попробуете вызвать padLeft(), предоставив что-либо, кроме string или number. Это также устранит необходимость выбрасывания исключения.

Листинг 2.8. padLeft с объединенным типом

 

Теперь вызов padLeft() с неверным типом для второго аргумента (например, true) вернет ошибку компиляции:

console.log( padLeft("Hello world", true)); // compilation error

СОВЕТ Если нужно объявить переменную, которая может содержать значения нескольких типов, не используйте тип any; используйте объединение вроде let padding: string | number. Еще один способ — это объявить две отдельные переменные: let paddingStr: string; let paddingNum: number.

Давайте изменим код в листинге 2.8, чтобы показать тип never, добавив случай else к инструкции if. Следующий листинг показывает, как модуль проверки типов выведет тип never для невозможного значения.

Листинг 2.9. Тип never невозможного значения

function padLeft(value: string, padding: string | number ): string {

    if (typeof padding === "number") {

        return Array(padding + 1).join(" ") + value;

    }

    if (typeof padding === "string") {

        return padding + value;

    }

    else {

        return padding;  

  Этот блок else никогда не выполняется

    }

}

Поскольку мы объявили в сигнатуре функции, что аргумент padding может быть либо string, либо number, любое другое значение для padding будет невозможным. Другими словами, случай else невозможен, и модуль проверки типов для переменной padding в случае else выведет тип never. Вы можете увидеть это сами, скопировав код из листинга 2.9 в игровую площадку TypeScript и наведя курсор на переменную padding.

ПРИМЕЧАНИЕ Еще одно преимущество объединенных типов в том, что IDE имеют функцию автоподстановки, которая предложит допустимые типы аргументов, и вы уже не сможете сделать в них ошибку.

Сравните код функций padLeft() в листингах 2.6 и 2.9. Каковы основные преимущества использования объединения string | number вместо типа any во втором аргументе? Если вы используете объединение, компилятор TypeScript предотвратит некорректные вызовы padLeft(), сообщив об ошибке во время компиляции.

Здесь мы использовали только объединение примитивных типов (string и number), но в следующем разделе вы увидите, как объявлять пользовательские объединенные типы.

2.2. Определение пользовательских типов

TypeScript позволяет вам создавать пользовательские типы с помощью клю­чевого слова type, объявлением класса или интерфейса либо объявлением enum (рассматривается в главе 4). Мы начнем знакомство с ключевого слова type.

2.2.1. Использование type

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

Листинг 2.10. Объявление псевдонимов типов Foot и Pound

type Foot = number;

type Pound = number;

Вы можете создать новый тип Patient и использовать приведенные псевдонимы в его объявлении.

Листинг 2.11. Объявление нового типа, использующего псевдонимы

type Patient = {  

  Объявляет тип Patient

    name: string;

    height: Foot;  

  Использует псевдоним Foot

    weight: Pound;  

  Использует псевдоним Pound

}

Объявления псевдонимов типов не генерируют код в компилированном JavaScript. В TypeScript объявление и инициализация переменной типа patient может выглядеть так:

Листинг 2.12. Объявление и инициализация свойств типа

let patient: Patient = {  

  Мы создаем экземпляр, используя нотацию объектного литерала

    name: 'Joe Smith',

    height: 5,

    weight: 100

}

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

Листинг 2.13. Забыли добавить свойство weight

let patient: Patient = {

    name: 'Joe Smith',

    height: 5

}

TypeScript выдаст ошибку:

"Тип '{ name: string; height: number; }' не может быть присвоен типу 'Patient'.

  Свойство 'weight' упущено в типе '{ name: string; height: number; }'."

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

Листинг 2.14. Объявление опциональных свойств

type Patient = {

    name: string;

    height: Height;

    weight?: Weight;  

  Свойство weight опционально

}

 

let patient: Patient = {  

  Переменная patient инициализирована без weight

    name: 'Joe Smith',

    height: 5

}

СОВЕТ Вы можете использовать вопросительный знак для определения опциональных свойств также в классах или интерфейсах, с которыми познакомитесь в этом же разделе чуть позже.

Еще можно использовать ключевое слово type, чтобы объявлять псевдонимы типов для сигнатуры функции. Представьте, что пишете фреймворк, который должен позволять создавать элементы управления и присваивать им функции-валидаторы. Функция-валидатор должна иметь конкретную сигнатуру — она должна принимать объект типа FormControl и возвращать либо объект, описывающий ошибки значения элемента управления, либо null, если значение допустимо. Можно объявить новый тип ValidatorFn так:

type ValidatorFn =

    (c: FormControl) => { [key: string]: any }| null

Здесь { [key: string]: any } означает объект, который может иметь свойства любого типа, но ключ (key) должен либо иметь тип строки, либо допускать преобразование в строку.

Конструктор FormControl может иметь параметры для функции-валидатора и использовать пользовательский тип ValidatorFn следующим образом:

class FormControl {

 

    constructor (initialValue: string, validator: ValidatorFn | null) {...};

}

СОВЕТ В приложении можно найти синтаксис для объявления опциональных ­параметров функций в JavaScript. Предыдущий фрагмент кода показывает способ объявления опционального параметра в TypeScript при помощи объединенного типа.

2.2.2. Использование классов в качестве пользовательских типов

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

JavaScript не имеет синтаксиса для объявления свойств классов, но в TypeScript такой синтаксис присутствует. На рис. 2.2 слева вы можете видеть, как мы объявили и инстанцировали класс Person с тремя свойствами. Справа видна версия ES6 кода, произведенного компилятором TypeScript.

 

Рис. 2.2. Класс Person, скомпилированный в JavaScript (ES6)

В версии JavaScript нет свойств. Также, поскольку класс Person не объявляет конструктор, пришлось инициализировать его свойства после инстанцирования. Конструктор — это особая функция, выполняемая один раз при создании экземпляра класса.

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

TypeScript предлагает квалификаторы уровня доступа public, private и protected (рассматриваются в главе 3), и если вы используете любой из них с аргументами конструктора, компилятор TypeScript сгенерирует код для добавления этих аргументов в качестве свойств в скомпилированный объект JavaScript (рис. 2.3).

 

Рис. 2.3. Класс Person с конструктором

Теперь код класса TypeScript (слева) более лаконичен, а сгенерированный код JavaScript создает три свойства в конструкторе. Обратите внимание на строку 6 на рис. 2.3 слева. Мы объявили константу, не указав для нее тип, но также можем переписать эту строку, обозначив тип p явно:

const p: Person = new Person("John", "Smith", 25);

Такая аннотация типа здесь необязательна. Так как вы объявляете константу p и тут же инициализируете ее с объектом известного типа (Person), модуль проверки типов сможет легко вывести и присвоить ее тип. Сгенерированный код JavaScript будет выглядеть так же, независимо от того, укажете вы тип p или нет. Вы можете увидеть, как инстанцировать класс Person без явного объявления его типа, в интерактивной среде TypeScript по адресу http://mng.bz/zlV1. Наведите курсор на переменную p — ее тип отобразится как Person.

СОВЕТ На рис. 2.3 с каждым аргументом конструктора класса мы использовали уровень доступа public. Это означает, что к соответствующим сгенерированным свойствам может обратиться любой код, расположенный как внутри, так и вне класса.

Когда вы объявляете свойства класса, то можете также отметить их как readonly. Такие свойства могут быть инициализированы либо в точке объявления, либо в конструкторе класса, после чего их значения уже не допускают изменения. Квалификатор readonly аналогичен ключевому слову const, но последнее не может быть использовано со свойствами класса.

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

Листинг 2.15. Свойства класса Block

 

Класс Block включает шесть свойств readonly. Обратите внимание, что нам не нужно явно объявлять свойства класса для аргументов конструктора, которые имеют квалификаторы readonly, private, protected или public, как мы делали бы в других объектно-ориентированных языках. В листинге 2.15 два свойства класса объявлены явно, а четыре неявно.

2.2.3. Интерфейсы в качестве пользовательских типов

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

TypeScript осуществляет поддержку интерфейсов с помощью ключевых слов interface и implements, но сами интерфейсы в JavaScript-код не компилируются. Они только помогают избежать использования неверных типов в процессе разработки. Давайте познакомимся с использованием ключевого слова interface для объявления пользовательского типа.

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

Листинг 2.16. Объявление пользовательского типа с помощью интерфейса

interface Person {

    firstName: string;

    lastName: string;

    age: number;

}

Скрипт в левой части рис. 2.2 объявляет аналогичный пользовательский тип Person, но при помощи ключевого слова class. В чем же разница? Если вы объявите пользовательский тип как class, то сможете использовать его как значение, то есть сможете инстанцировать его, используя ключевое слово new, как это показано на рис. 2.2 и 2.3.

Помимо этого, при использовании class в коде TypeScript в сгенерированном JavaScript-коде появится соответствующий ему код (функция в ES5 и класс в ES6). Если же вы используете ключевое слово interface, никакой соответствующий ему код в JavaScript создан не будет (рис. 2.4).

 

Рис. 2.4. Пользовательский тип Person как интерфейс

В правой части рис. 2.4 нет никакого упоминания интерфейса и JavaScript более лаконичен, что хорошо для любого развертываемого кода. Однако в процессе разработки компилятор проверит, чтобы объект, предоставленный в качестве аргумента функции savePerson(), включал все свойства, объявленные в интерфейсе Person.

Какое ключевое слово использовать: type, interface или class?

Мы показали вам, что пользовательский тип может быть объявлен с помощью ключевых слов type, class либо interface. Какое же из них лучше всего использовать для объявления пользовательского типа Person?

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

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

interface Person {

    name: string;

}

 

function getP(p: Person){

      if (p instanceof Person){ // ошибка компиляции

     }

}

Модуль проверки типов выдаст ошибку, что Person относится только к типу, но используется здесь как значение.

Если вы объявляете пользовательский тип просто для дополнительной безо­пасности, предлагаемой модулем проверки типов в TypeScript, используйте type или interface

...