Поговорим про введение в TypeScript и то, как с помощью него мы можем сделать наше программирование и наш код на JavaScript более надежными и прогнозируемыми. 

Меня зовут Александр Чернов, я фронтенд-разработчик в Альфе, веду подкаст «ТИНОИД», обожаю плавание и влюблён в веб и фронтенд-разработку.

Всё ли так хорошо в мире программирования на JavaScript? 

Представим, что у нас есть простая функция add, которая принимает два неких параметра и выполняет сложение или конкатенацию, если мы передаем строки. 

В примере мы передаём числа и ожидаем, что программа не сломается и с ней всё будет в порядке, надеемся, молимся. 

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

Давайте посмотрим, что будет, если мы наведём курсор на функцию и посмотрим на подсказку от IDE.

IDE говорит, что здесь есть функция add, которая принимает параметры a и b и что-то возвращает. И это «что-то», так называемое обозначение, подписано как any

В контексте примера any говорит о том, что наша функция add и наша среда разработки понимает, что здесь мы можем передать что угодно, потому что any — это отсутствие какого-то определенного типа. Даже если мы ничего не передадим, то не увидим какой-либо ошибки, ведь мы ничего конкретно не обозначили.

В следующем примере уже другая функция — double

Мы ожидаем принять параметр a, но при записи выполнения её результата (на пятой строке) мы не передаем аргумент.

Фактически, мы должны были бы получить ошибку о том, что мы что-то ожидаем от функции, но ничего не передаём. Но так как тип обозначения any, то наша функция не будет ругаться на то, что мы что-то забыли, даже если мы оставим аргументы пустыми.

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

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

JSDoc

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

Соответственно, чтобы описать какой-либо параметр, нам необходимо включить вот такую конструкцию: 

  • Описать в param то, что мы ожидаем принять, аргумент, и тип этого аргумента. В данном случае аргумент a типа number.

  • В returns мы пишем, что мы вернём. В моём примере мы ожидаем, что тоже вернём какое-то число.

  • И в description описываем, что это такое: как с функцией работать, как мы можем её использовать.

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

Иными словами, JSdoc нас не спасёт от поиска ошибок во время разработки. 

Плавно подходим к TypeScript 

Языки программирования можно поделить на две категории: с динамической типизацией и статической.

 

В языках программирования с динамической типизацией, как JavaScript, на этапе написания года мы не знаем как и с какими данными работает наша программа и какие типы у этих данных.

В языках со статическое типизацией, в TypeScript, проверка кода на корректность/ошибки проводится до запуска кода,.

TypeScript — это надмножество — (расширение/надстройка) JavaScript, которое добавляет типы (программные).

TS — это тот же JS, но с типами. 

Типы — это множество значений, которыми мы можем описать наши переменные, функции. Типы в TypeScript используются для определения и описания данных, которые могут быть определены и использованы в программе. Мы можем использовать типы значений, такие как number, boolean, string и так далее.

Не буду долго теоретизировать, перейду сразу к примеру. 

Чем нам может помочь TS?

Чтобы использовать TS, нам необходимо добавить пакет в свой проект и проинициализировать его.
npm i typescript -D / npx tsc --init и обязательно поменять расширение файла на .ts
После этого наш TypeScript включается в работу. Конечно, его настраивать нужно немножко больше, но здесь я намеренно упростил процесс. 

И как только мы смотрим на наш код из прошлого примера с функцией double, TypeScript высвечивает ошибку. 

Мы видим, что наш параметр a на самом деле any: мы можем передать что угодно, а можем вообще не передавать. Для TypesScript это ошибка. Но главное, что мы видим её сразу же — на этапе написания кода. И сразу же можем понять, в чём ошиблись и как ошибку исправить. 

И просто так вызвать нашу функцию с пустыми аргументами мы уже не сможем, потому что нам надо явно что-то указать: мы ожидаем туда передать один аргумент, а передаём ноль — это ошибка.

Давайте посмотрим пример того, как мы можем типизировать нашу функцию double

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

И теперь при вызове нашей функции, если мы обратимся к подсказкам нашей IDE и наведём мышку на вызов функции double, мы увидим, что она ожидает явно уже не any, а тип number у аргумента, и вернем мы также number

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

Где еще нам пригодится TS? Рассмотрим одну типовую задачу.

Использование TypeScript с API

Рассмотрим пример получения данных с какого-то эндпойнта и их отображения в нашей верстке. На иллюстрации пример на JS (React). 

Здесь мы заводим некоторый стэйт, который будет хранить массив наших пользователей. В useEffect мы делаем запрос к эндпойнту, и затем в JSX-разметке по условию возвращаем наш массив пользователей. 

Когда я пишу атрибут key я бы хотел, чтобы моя IDE подсказала, какие поля я могу использовать у моего пользователя. Если я попробую вызвать подсказку в данном примере на JSX-расширении без TypeScript, то, к сожалению, я не получу ничего путного: появятся какие-то системные сообщения и типы, которые не помогут мне в разработке.

Как это можно исправить? Для начала необходимо обратиться к документации нашего бэкенда и узнать какие типы и JSON мы можем получить с нашего эндпойнта. Это можно делать с помощью Swagger / Описания в документации или тех инструментов, что вы используете в команде или просто сходить на эндпойнт и посмотреть, какой JSON мы получаем.

В данном случае мы видим, что у нас есть массив объектов user с определёнными полями, которые мы точно должны получить. 

Давайте возьмем парочку из них. Например, для атрибута key нам подойдёт id и, допустим, имя name

Чтобы наш компонент типизировать нам необходимо, естественно, поменять расширение на .tsx, а затем завести специальный тип User. И описать его вот в таком синтаксисе. 

Мы явно указываем, что наш тип User будет в себе содержать id с типом number и name с типом string:

После этого мы можем сразу типизировать наш стейт. Для этого скажем ему, что теперь он может принять не просто некий null (как начальное значение), но также должен учитывать, что в типе может прийти либоnull при ошибке запроса, либо массив объектов с типом user, который мы описали выше.

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

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

Про d.ts файлы

Бывает и так, что мы типизировали свой код, но потом используем чужой не типизированный модуль/компонент API. Это довольно частая проблема, особенно если мы используем какую-то непопулярную библиотеку для решения. Здесь есть решение — d.ts файлы. Файлы с расширением .d.ts содержат декларации типов для TypeScript, которые содержат описание типов без реализации. Они позволяют компилятору TypeScript понимать структуру данных, функций и классов, которые не были написаны на TypeScript (например, библиотеки JavaScript).

Использование TypeScript с Props

Ещё один частый кейс — применение TypeScript в контексте props. 

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

Например, я хочу описать какой-то props. Я буду ожидать, что в мой компонент UsersList я буду передавать какой-то заголовок типа header. Для этого описываю специальный тип Props и говорю ему, что в нём будет содержаться поле header с типом string, то есть строкой. Также я этот тип использую в описании к типу Props уже в самом компоненте. 

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

Мне не нужно запускать программу или держать в голове те props, которые я бы хотел использовать — я могу «забыть» их написать, а с TypeScript - всегда увижу ошибку и смогу поправить. Удобно. Главное не забывать описывать props!

Подытожим 

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

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

Читаемость кода. Чётко определенные типы данных делают код более понятным как для меня, так и для других разработчиков, работающих над проектом. Это особенно важно при работе в команде или над долгосрочными проектами. Становится проще погрузиться в проект и понять, какой здесь поток данных, что мы с ними делаем. В большие проекты на JS довольно трудно быстро «въехать», но TS помогает и с этим — ты видишь, какие данные какой компонент принимает, и можешь построить логическую цепочку. На «чистом» JS всё витает где-то в воздухе. 

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

Более надежные API. TypeScript помогает создавать более надежные и предсказуемые API, как внутри компонента, так и между компонентами. Например, я могу точно определить тип данных, которые принимает и возвращает функция, что минимизирует риск возникновения непредвиденных ошибок, и другие разработчики так же быстро смогут понять, что возвращается на сервер и что с этим можно сделать.

Если вам нужна статическая типизация, если хочется больше понимать своё приложение, иметь больше надёжности, то рекомендую. Да, конечно старт проекта с использованием TS не такой быстрый (хотя сейчас существуют очень много инструментариев для TypeScript, большое кол-во автоматизаций), но это определенно точно хорошая инвестиция в проект!

Правда если вы решили внедрять TS на проект, что работает долгие годы и уже на поддержке, то, возможно, не стоит тратить ресурсы на то, чтобы добавить TypeScript. Здесь уже бизнес будет решать, выгодно это или нет. А разработчики могут использовать например JSDoc.

В качестве материалов для изучения TypeScript я могу посоветовать следующие:

А также отличный онлайн-тренажер с возможностью выбора уровня сложности.

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


  1. meonsou
    11.10.2024 15:17

    Про JSDoc вводите в заблуждение немного, сам же тайпскрипт умеет проверять по нему код. Собственно, он вывел в подсказке типы из комментария потому что прочитал его. Достаточно включить allowJs/checkJs или написать // @ts-check и оно будет ругаться.


  1. Nurked
    11.10.2024 15:17

    Typescript это, возможно, самый "врущий" язык, с которым мне приходилось работать.

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

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

    В TS тебе приходится тратить больше времени на мороку с типами передаваемых переменных, и в три раза больше времени на мороку с типами стандартных переменных.

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

    При этом, в скорости это нифига не выигрывает, и просто добавляет ложку мёда в цистёрну дёгдя йаваскрипта.

    Решение для типизации было следующим - сервер, написаный на golang, который возвращал объект нужного типа. (40 строк кода). Смысл TS быстро попадает, когда в мире столько других замечательных языков программирования.


    1. bitbucked
      11.10.2024 15:17

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


    1. meonsou
      11.10.2024 15:17

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

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


    1. dom1n1k
      11.10.2024 15:17

      Я называю TS языком-тамагочи - постоянно требует внимания и обслуживания его жизнедеятельности.


    1. LbISS
      11.10.2024 15:17

      Если есть типы с кучей Omit, Pick, Partial и т.п. - скорее всего, стоит на проекте стоит навести порядок в архитектуре. Независимо от размера проекта.

      У меня есть проект в полсотни микросервисов с микрофронтами, легаси и оркестратором использование всех этих модификаторов скорее всего не превышает 20 штук в корнер кейсах. Всегда есть внешние модели - ДТО с АПИ или внешних либ и внутренние сущности, которые реально отображаются и отражают семантику конкретного экрана, и слой маппинга между ними. Омитам при таком варианте взяться неоткуда, всегда есть только один понятный тип, без обрезков.

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


  1. ParaMara
    11.10.2024 15:17

    Недавно была статья на Хабре где недоавтор обсуждал covariant в Dart будучи нулевым в ООП. Теперь эта статья где недоавтор обсуждает TypeScript будучи столь же нулевым в структурном программировании и, я бы на это поставил баночку икры, ООП тоже.

    TypeScript, как и всё (при встрече я бы пинками затолкал вам это в глотку - ВСЁ) новомодное, создан с единственной целью - продать корпорациям ещё одну идею как сэкономить на программистах и навариться. При появлении идеи n, где n > 1, любому математику (существу, которому дозволено размышлять вне системы джун - мидл - тестер и вот этого всего) понятно, что идеи с 1 по n-1 провалились, если в бинарной логике. А TypeScript - далеко не последняя идея…

    Это было введение. Теперь по тексту.

    С какого перепугу функция square или power2 называется double? Это важно, потому что имелись рекомендации - называть функцию понятным именем и писать так, чтобы её можно было быстро прочитать и понять. Никакие подсказки IDE не нужны… но умеющие придумывать и писать программисты точно не самые дешёвые.

    С какого перепугу человек написал double( и ждёт подсказок от IDE? Слышал гром и не знает где он? Аналогично user - у проекта есть данные, у данных есть структура, в структуре решено что есть id и name, структура документирована, можно посмотреть (нужно если хочется понимать чего делаешь) и средства языка вообще без надобности.

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

    Кстати, с какого перепугу функция возведения во вторую степень обязана генерировать ошибку при вызове без аргументов?

    Кстати, с какого перепугу при наличии WebAssembly TypeScript незаменимый? Есть же модный Rust…


    1. Mox
      11.10.2024 15:17

      Раньше я был молодой и дерзкий и писал без TypeScript. Потом попробовал - заметил как компилятор в процессе редактирования поймал несколько ошибок и обратно на JS не хочется.


      1. ArutaGerman
        11.10.2024 15:17

        Можно писать на js так же: с подсветкой ошибок и выводом их в консоль, + подсказки работают в ide, только не будет крашиться приложение, если тип ошибочен.

        Это достигается связкой Ts + jsdock + включенная валидация js в vs code

        Но! При этом мы пишем js, а не ts и не имеем той массы условностей и многословности, которые заставляет нас делать ts

        Ts установлен в dev dep и служит для того для чего и нужен: сопоставляет типы


    1. 0xBAB10
      11.10.2024 15:17

      интересные голословные заявления. возможно товарищ "высокоранговый" аналитег покажет пруфы где, в каких конкретно кейсах, на ts наварились microsoft? или пруфы что идеи ts провалились?


  1. ooko
    11.10.2024 15:17

    Много лет пишу большие проекты на js. У меня нет описанных проблем - что я делаю не так, помогите

    В свете развития ИИ думаю типы отомрут


    1. MountainGoat
      11.10.2024 15:17

      Пока что ИИ с прописанными типами работает заметно лучше, чем без них. Во втором случае он очень полагается на имена переменных. То есть, если он увидит let number_of_people = "пять"; то спокойно напишет number_of_people += 1 потому что это number;


      1. ooko
        11.10.2024 15:17

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


        1. MountainGoat
          11.10.2024 15:17

          Ну как рисуют. Вот например из сегодняшнего.

          Скрытый текст


          1. ooko
            11.10.2024 15:17

            А вы ты использовали?


            1. MountainGoat
              11.10.2024 15:17

              Скрытый текст


      1. ooko
        11.10.2024 15:17

        Кстати это хороший пример почему еще не нравится js. Давно заметил что джуны (ну и мидлы) порой пытаются сделать задачу наугад ровно как сейчас делает ИИ - лишь бы было похоже на нужный результат. Так вот в js такая халтура видна лучше чем в ts


  1. supercat1337
    11.10.2024 15:17

    Автору фронтенд-разработчику необходимо почитать https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html. Будет крайне познавательно. Уже давно можно в JavaScript с типизацией. Плюс не забывайте о d.ts (если не хватило jsdoc).

    Вопрос в том, что вы ещё используете в работе. Если все инструменты требуют ts, то оно и понятно, что с ts не слезете, даже если разлюбите его.


  1. Keeper10
    11.10.2024 15:17

    TypeScript

    Надежное программирование

    Распространённое заблуждение.


  1. Notactic
    11.10.2024 15:17

    Трижды проверил дату статьи. Очень своевременно, для чего нужна - загадка :D

    Тайпскрипт уже лет 10, стандарт де-факто(рыночек зарешал). Современный фронтендер без ts - нормальной работы не найдет.


  1. Vitaly_js
    11.10.2024 15:17

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

    А то у вас все получилось просто замечательно, типа, дерзайте. Но при этом:

    Правда если вы решили внедрять TS на проект, что работает долгие годы и уже на поддержке, то, возможно, не стоит тратить ресурсы на то, чтобы добавить TypeScript. Здесь уже бизнес будет решать, выгодно это или нет. А разработчики могут использовать например JSDoc.

    Что из статьи должно было подвести к такому выводу? Например, взять и не включать strict, тогда и ваши примеры с any не будут вызывать ошибок на тс. Что это за "ресурсы" такие и каким образом они на самом деле влияют на обозначенные вами 4 положительных стороны?

    А то получается у нас "Введение в мир надежного программирования" через тс, но один из выводов - это не использовать тс, а использовать JSDOC.

    Может не все так радужно, и по "тудушке" как раз и не заметно какие трудности будут на пути к надежному программированию на тс.

    По сути, вы привели ссылку на статью "Практическое руководство по ТС для разработчиков" и там как заключение:

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

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

    Просто пробежимся по форме:

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

    Легко поддерживаемых? Никакой тс не компенсирует использование антишаблонов, которые приведут к неподдерживаемому коду.

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

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

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

    Читаемость кода. Чётко определенные типы данных делают код более понятным как для меня, так и для других разработчиков, работающих над проектом. Это особенно важно при работе в команде или над долгосрочными проектами. Становится проще погрузиться в проект и понять, какой здесь поток данных, что мы с ними делаем. В большие проекты на JS довольно трудно быстро «въехать», но TS помогает и с этим — ты видишь, какие данные какой компонент принимает, и можешь построить логическую цепочку. На «чистом» JS всё витает где-то в воздухе. 

    К этому отрезку тоже можно применить большую часть вопросов выше. Но у меня тут дополнение другого плана. На "чистом" js все не витает в воздухе. У вас будет и качественный нейминг. И документация в свагере. А если есть тесты с библиотекой тестовых утилит и генераторов данных, то просто читая spec файлы вы можете узнать даже больше, чем знакомясь посредством исходного кода на ts (разумеется не всегда, просто для примера сказал)

    Более надежные API. TypeScript помогает создавать более надежные и предсказуемые API, как внутри компонента, так и между компонентами. Например, я могу точно определить тип данных, которые принимает и возвращает функция, что минимизирует риск возникновения непредвиденных ошибок, и другие разработчики так же быстро смогут понять, что возвращается на сервер и что с этим можно сделать.

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

    Поймите меня правильно, тс действительно нужен, но у него есть определенный порог вхождения. И познакомиться именно с этим порогом - это действительно путь к надежному программированию.