TypeScript с нуля - полный курс и паттерны проектирования

Курс охватывает все аспекты языка: от настройки окружения и базовых типов до продвинутых техник, Generics и декораторов. Особое внимание уделено паттернам проектирования и созданию реального проекта — утилиты выполнения команд [purpleschool.ru](https://purpleschool.ru/course/typescript).

1. Введение, настройка окружения и работа с типами

Введение, настройка окружения и работа с типами

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

!TypeScript является надмножеством JavaScript: любой JS-код валиден в TS, но TS добавляет новые возможности

Почему TypeScript стал стандартом индустрии

JavaScript — язык с динамической типизацией. Это означает, что переменная может содержать число, затем строку, а затем функцию. Ошибки, связанные с типами (например, попытка вызвать метод массива у числа), обнаруживаются только в момент выполнения программы (runtime). TypeScript переносит обнаружение этих ошибок на этап написания кода и компиляции (compile time).

Основные преимущества:

* Статическая типизация: Позволяет описывать форму объекта, аргументы функций и возвращаемые значения. * Поддержка IDE: Автодополнение, навигация по коду и рефакторинг работают значительно точнее. * Читаемость: Типы служат самодокументацией кода. * Совместимость: TypeScript компилируется в любую версию ECMAScript (ES3, ES5, ES6+), что позволяет использовать современные фичи языка даже в старых окружениях.

Настройка окружения

Для работы с TypeScript необходимо установить Node.js, так как компилятор написан на JS и распространяется через пакетный менеджер npm.

Установка компилятора

Установите TypeScript глобально, чтобы команда tsc была доступна в терминале:

Проверьте установку:

Инициализация проекта

Создайте папку проекта и выполните инициализацию конфигурационного файла:

Команда создаст файл tsconfig.json. Это «сердце» настроек компилятора. В нем можно указать, в какую версию JS компилировать код (target), где лежат исходники (rootDir) и куда складывать готовые файлы (outDir).

Пример базовой настройки tsconfig.json:

Компиляция первого файла

Создайте файл src/index.ts:

Запустите компиляцию:

Компилятор считает настройки из tsconfig.json, возьмет файл из src и создаст dist/index.js, который уже можно запустить через node dist/index.js.

Основные типы данных

В TypeScript типизация осуществляется через аннотации: после названия переменной ставится двоеточие и тип.

Примитивные типы

TypeScript поддерживает все примитивы JavaScript:

* number: Для любых чисел (целых и с плавающей точкой). * string: Для строк (одинарные, двойные кавычки и шаблоны). * boolean: Значения true или false.

Массивы (Arrays)

Существует два способа типизации массивов:

  • Использование квадратных скобок type[].
  • Использование обобщенного типа (Generic) Array<type>.
  • Если массив должен содержать разные типы (что не рекомендуется, но возможно), используется объединение типов (Union Type) или кортежи.

    Кортежи (Tuples)

    Кортеж — это массив фиксированной длины, где известен тип каждого элемента на конкретной позиции.

    Тип Any

    Тип any отключает проверку типов для переменной. Это «аварийный выход», который позволяет работать с кодом так же, как в чистом JavaScript. Использовать его следует с осторожностью, так как он сводит на нет преимущества TypeScript.

    > Использование any часто считается плохой практикой. В строгом режиме ("strict": true в конфиге) TypeScript будет стараться предотвратить неявное использование any.

    Функции

    В функциях типизируются аргументы и возвращаемое значение.

    Если функция ничего не возвращает, используется тип void:

    Объекты

    Для типизации объектов можно описывать их структуру инлайн или использовать псевдонимы типов (Type Aliases) и интерфейсы (Interfaces), которые мы подробно разберем в следующих статьях. Пока рассмотрим базовую структуру:

    Unknown

    Тип unknown — это безопасный аналог any. Если переменная имеет тип unknown, вы не можете совершать с ней операции (вызывать методы, складывать), пока не сузите тип (не докажете компилятору, что там лежит, например, число).

    Вывод типов (Type Inference)

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

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

    Итоги

  • TypeScript — это надстройка над JavaScript, добавляющая статическую типизацию и требующая компиляции через tsc.
  • Конфигурация: Файл tsconfig.json управляет параметрами компиляции, такими как целевая версия JS и строгость проверок.
  • Базовые типы: Включают примитивы (number, string, boolean), массивы, кортежи и специальные типы (any, void, unknown).
  • Безопасность: Старайтесь избегать any, предпочитая unknown или конкретные типы для сохранения надежности кода.
  • 2. Классы, компилятор и Generics

    Классы, компилятор и Generics

    В современной разработке на TypeScript умение создавать гибкие и безопасные архитектурные решения базируется на трех китах: объектно-ориентированном подходе (классы), понимании работы инструментов сборки (компилятор) и способности писать переиспользуемый код (Generics). В этой статье мы объединим эти концепции, чтобы научиться создавать масштабируемые компоненты.

    Классы в TypeScript: больше, чем просто синтаксический сахар

    Классы в JavaScript (начиная с ES6) предоставляют удобный синтаксис для создания объектов и реализации наследования. TypeScript расширяет эту концепцию, добавляя строгую типизацию полей и модификаторы доступа, которые привычны разработчикам на C# или Java.

    Поля и модификаторы доступа

    В отличие от JavaScript, в TypeScript вы обязаны объявлять поля класса до их использования в конструкторе. Это позволяет компилятору знать о структуре объекта заранее.

    TypeScript предоставляет три основных модификатора доступа:

  • public (по умолчанию): Свойство доступно везде.
  • private: Свойство доступно только внутри класса. При попытке обращения снаружи возникнет ошибка компиляции.
  • protected: Свойство доступно внутри класса и в его наследниках.
  • Важно понимать разницу между private в TypeScript и приватными полями #field в современном JavaScript. Модификатор private в TS работает только на этапе проверки типов. После компиляции в JavaScript (если цель ES5 или ES6) этот код превратится в обычное свойство, доступное в runtime. Если вам нужна настоящая приватность в runtime, используйте синтаксис # (например, #id), который поддерживается в новых версиях JS.

    !Области видимости модификаторов доступа в TypeScript

    Роль компилятора (tsc)

    Компилятор TypeScript (tsc) не просто преобразует код в JavaScript. Он выполняет статический анализ, предотвращая ошибки, которые в обычном JS всплыли бы только при запуске приложения.

    Стирание типов (Type Erasure)

    Ключевая особенность работы компилятора — стирание типов. Интерфейсы, типы (type aliases) и дженерики существуют только в момент написания кода и компиляции. В итоговом .js файле их нет.

    Рассмотрим пример:

    После компиляции интерфейс ILogger исчезнет полностью. Это означает, что вы не можете использовать типы TypeScript для проверок во время выполнения (например, нельзя написать if (obj instanceof ILogger)).

    Настройки строгости

    В файле tsconfig.json есть флаг strictPropertyInitialization. Если он включен (что рекомендуется), компилятор будет следить, чтобы все поля класса были инициализированы либо в момент объявления, либо в конструкторе. Это спасает от классической ошибки undefined is not an object.

    Generics (Обобщения): гибкость без потери контроля

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

    Проблема без дженериков:

    Используя any, мы теряем информацию о типе. Если мы передадим число, компилятор не запомнит, что вернулось именно число.

    Решение с дженериками:

    Здесь T — это переменная типа. Мы захватываем тип, который передал пользователь (например, string), и используем его как возвращаемый тип.

    Обобщенные классы (Generic Classes)

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

    Согласно материалам ru.hexlet.io, дженерик-классы позволяют создавать компоненты, работающие с разными типами данных, сохраняя гарантии безопасности. Например, класс Triple может хранить три значения любого типа, и компилятор будет точно знать, какой тип находится в каждом поле.

    Пример: Реализация Стека

    Создадим класс Stack, который работает по принципу LIFO (Last In, First Out).

    В этом примере <T> после имени класса указывает, что класс является обобщенным. При создании экземпляра new Stack<number>() мы фиксируем T как number для этого конкретного объекта.

    Как отмечает code.mu, если вы создали объект обобщенного класса и задали ему тип (явно или через вывод типов конструктора), вы не сможете случайно перезаписать свойство значением другого типа. Это обеспечивает надежность кода на этапе написания.

    Ограничения дженериков (Constraints)

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

    Представим, что нам нужно работать только с объектами, у которых есть свойство length.

    ``typescript interface Lengthwise { length: number; }

    class Logger<T extends Lengthwise> { log(item: T): void { console.log(Item length: T(n)O1O(1)O(n)$) лежит на разработчике.

    Итоги

  • Классы в TS предоставляют строгую структуру с модификаторами public, private, protected, которые работают на этапе компиляции для защиты инкапсуляции.
  • Компилятор стирает типы при сборке проекта. Все проверки происходят до запуска кода. Настройки tsconfig.json напрямую влияют на строгость проверок классов.
  • Generics позволяют создавать переиспользуемые компоненты (функции и классы), которые адаптируются под конкретный тип данных в момент использования, сохраняя типобезопасность.
  • Обобщенные классы являются идеальным инструментом для создания коллекций и оберток данных, предотвращая смешивание разных типов в одной структуре.