Мы подошли к кульминационному моменту нашего изучения TypeScript. Если предыдущие 39 уроков были похоже на сбору пазла по кусочкам, то сегодня мы соберём всю картину воедино. Мы будем настраивать профессиональный рабочий процесс для современной frontend-разработки, используя связку TypeScript + Webpack. Это не просто финальный урок, это ваш пропуск в мир реальных коммерческих проектов, где код должен быть не только правильным, но и эффективно собран, оптимизирован и готов к развёртыванию.
До этого момента мы с вами писали код, компилировали его с помощью tsc и запускали в браузере с помощью простых скриптов. Это прекрасно работало для небольших примеров и обучения. Однако в реальном мире всё устроено сложнее. Современное веб-приложение состоит из десятков, сотен, а то и тысяч файлов TypeScript, модулей, стилей, изображений и других ресурсов. Браузер не умеет «на лету» работать с системой модулей ES6, не говоря уже о TypeScript. Ему нужны единые, минифицированные файлы JavaScript, CSS и HTML.
Именно здесь на сцену выходят инструменты сборки или бандлеры. Webpack это самый популярный и мощный из них. Он выступает в роли управляющего. Он берёт все ваши модули (TypeScript, JS, CSS, картинки, шрифты), анализирует их зависимости, преобразует их с помощью специальных инструментов (называемых лоадерами) и упаковывает в одно или несколько оптимизированных «бандлов» (пакетов), готовых к исполнению в браузере. Использование TypeScript без подобного инструмента сегодня, это как пытаться построить небоскрёб с помощью только молотка и гвоздей. Технологически возможно, но неэффективно, медленно и ненадёжно.
Подготовка проекта
Прежде чем мы погрузимся в конфигурационные файлы, давайте подготовим нашу рабочую директорию. Мы создадим проект с нуля, чтобы вы понимали каждый этап. Откройте ваш терминал и выполните последовательность команд.
# Создаём новую директорию для проекта и переходим в неё mkdir my-ts-app cd my-ts-app # Инициализируем новый проект npm. Флаг -y соглашается со значениями по умолчанию npm init -y # Создаём структуру папок mkdir src mkdir public mkdir dist # Создаём основные файлы touch src/index.ts touch src/modules/calculator.ts touch public/index.html touch webpack.config.js touch tsconfig.json
Теперь давайте взглянем на структуру нашего проекта, которая у нас получилась:
my-ts-app/ ├── node_modules/ # (появится позже) Зависимости проекта ├── src/ # Исходный код на TypeScript │ ├── modules/ │ │ └── calculator.ts │ └── index.ts # Главный входной файл приложения ├── public/ # Статические файлы │ └── index.html # HTML-шаблон ├── dist/ # (появится позже) Собранные файлы для продакшена ├── package.json # Метаданные и зависимости проекта ├── webpack.config.js # Конфигурация Webpack └── tsconfig.json # Конфигурация TypeScript
Эта структура является общепринятым стандартом. src это наша «кухня», где мы пишем код. public это статические активы, которые не нужно обрабатывать (например, favicon). dist (от distribution) это конечный продукт, который мы будем загружать на сервер.
Инициализация TypeScript
У нас уже есть файл tsconfig.json. Давайте наполним его жизнью. Этот файл говорит компилятору TypeScript, как именно компилировать наш код.
{ "compilerOptions": { /* Базовые настройки */ "target": "ES2020", // В какую версию JS компилируем "module": "ESNext", // Система модулей для исходного кода "moduleResolution": "node", // Алгоритм разрешения модулей "lib": ["DOM", "ES2020"], // Какие библиотеки TS включает в контекст /* Строгие проверки TypeScript */ "strict": true, // Включает все строгие проверки типов "esModuleInterop": true, // Упрощает взаимодействие с CommonJS модулями "skipLibCheck": true, // Ускоряет компиляцию, пропуская проверку .d.ts файлов библиотек /* Настройки для модульного разрешения */ "baseUrl": "./src", // Базовая директория для разрешения не-относительных модулей "paths": { // Alias для путей импорта "@modules/*": ["modules/*"] }, /* Куда помещать скомпилированные файлы (важно для режима разработки с tsc) */ "outDir": "./dist", "rootDir": "./src" }, "include": ["src/**/*"], // Какие файлы компилировать "exclude": ["node_modules", "dist"] // Какие файлы игнорировать }
Обратите особое внимание на опцию "target": "ES2020". Мы компилируем в современный JavaScript, потому что Webpack и его лоадеры позаботятся о его преобразовании в код, понятный даже старым браузерам (через Babel, но сегодня мы его использовать не будем для упрощения). Параметр "paths" позволяет нам создавать красивые алиасы для импортов, чтобы вместо import { Calculator } from '../../modules/calculator' мы могли писать import { Calculator } from '@modules/calculator'.
Практическая задача 1: Создайте файл tsconfig.json с приведённой выше конфигурацией. Попробуйте изменить значение "target" на "ES5" и подумайте, какие различия это может вызвать в итоговом бандле.
Установка зависимостей
Теперь нам нужно установить все необходимые npm-пакеты. Мы разделим их на две группы: зависимости для разработки (devDependencies) и обычные зависимости (dependencies). Код, который мы пишем на TypeScript, после компиляции будет зависеть от каких-то библиотек (например, если бы мы использовали React), но сами инструменты сборки (TypeScript, Webpack) нужны только на этапе разработки.
Выполните в терминале следующую команду:
npm install --save-dev typescript webpack webpack-cli webpack-dev-server ts-loader html-webpack-plugin css-loader style-loader mini-css-extract-plugin clean-webpack-plugin
Давайте разберём, что за чем мы установили:
-
typescript: Ядро TypeScript, его компилятор. -
webpack: Сам бандлер, ядро системы. -
webpack-cli: Интерфейс командной строки для Webpack, позволяющий запускать его из терминала. -
webpack-dev-server: Легковесный сервер для разработки с горячей перезагрузкой (Hot Module Replacement). Это то, что делает разработку невероятно комфортной. -
ts-loader: Лоадер для Webpack. Его задача брать.tsи.tsxфайлы и пропускать их через компилятор TypeScript. -
html-webpack-plugin: Плагин, который автоматически внедряет собранные скрипты и стили в наш HTML-файл. -
css-loader&style-loader/mini-css-extract-plugin: Лоадеры для работы с CSS.css-loaderпозволяет импортировать CSS-файлы в JavaScript, аstyle-loaderвставляет эти стили в DOM тегом<style>.mini-css-extract-pluginэто более продвинутая альтернатива для продакшена, которая выносит CSS в отдельные файлы.
Настройка Webpack
Теперь самый важный этап, это настройка Webpack. Откройте файл webpack.config.js. По умолчанию Webpack ожидает найти здесь CommonJS-модуль, который экспортирует объект конфигурации.
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = (env, argv) => { // Проверяем, в каком режиме запущена сборка const isProduction = argv.mode === 'production'; const config = { // Точка входа в приложение. Webpack начнёт строить граф зависимостей отсюда. entry: './src/index.ts', // Куда и как помещать результат сборки output: { path: path.resolve(__dirname, 'dist'), // Абсолютный путь к директории filename: isProduction ? '[name].[contenthash].js' : '[name].js', // Имя файла бандла // [name] - имя чанка (по умолчанию 'main'), [contenthash] - хэш от содержимого файла }, // Настройки, влияющие на нормализацию путей и разрешение модулей resolve: { extensions: ['.ts', '.js'], // Какие расширения файлов разрешать импортировать без указания расширения alias: { '@modules': path.resolve(__dirname, 'src/modules/'), // Алиасы для путей, как в tsconfig.json }, }, // Модули и правила их обработки (лоадеры) module: { rules: [ // Правило для обработки TypeScript файлов { test: /\.ts$/, // Регулярное выражение: применяется ко всем файлам с расширением .ts use: 'ts-loader', // Какой лоадер использовать exclude: /node_modules/, // Что исключать из обработки }, // Правило для обработки CSS файлов { test: /\.css$/, use: [ isProduction ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader', ], // Важен порядок! Webpack применяет лоадеры справа налево. // Сначала 'css-loader' прочитает CSS-файл, затем 'style-loader' вставит его в DOM. // В продакшене вместо 'style-loader' используем MiniCssExtractPlugin для выноса CSS в отдельный файл. }, ], }, // Плагины - это классы с более широкими возможностями, чем лоадеры plugins: [ // Плагин, который очищает папку dist перед каждой сборкой new CleanWebpackPlugin(), // Плагин, который генерирует HTML-файл и автоматически добавляет в него ссылки на JS и CSS бандлы new HtmlWebpackPlugin({ template: './public/index.html', // Используем наш HTML как шаблон minify: isProduction, // Минифицировать HTML в продакшене }), // В продакшен-режиме добавляем плагин для извлечения CSS ...(isProduction ? [new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' })] : []), ], // Настройка сервера для разработки devServer: { static: { directory: path.join(__dirname, 'dist'), // Директория для раздачи статики }, port: 3000, // Порт open: true, // Автоматически открывать браузер hot: true, // Включить Hot Module Replacement (Горячую замену модулей) }, // Карты исходного кода (source maps) помогают в отладке, показывая оригинальный TS-код в инструментах разработчика devtool: isProduction ? 'source-map' : 'eval-cheap-module-source-map', }; return config; };
Эта конфигурация уже готовая, профессиональная настройка. Она использует функцию, которая принимает переменные окружения (env) и аргументы командной строки (argv). Это позволяет нам иметь единую конфигурацию для разработки и продакшена, динамически подстраивая её под режим. Ключевые различия:
-
Имена файлов: В продакшене мы используем
[contenthash]для обеспечения инвалидации кэша браузера. Если содержимое файла изменилось, изменится и его хэш и браузер скачает новую версию. -
CSS: В разработке стили вставляются через JS (быстро), а в продакшене выносятся в отдельный файл (оптимально для кэширования).
-
Source Maps: В разработке мы используем быстрые source maps (
eval-cheap-module-source-map), а в продакшене полноценные (source-map).
Практическая задача 2: Изучите конфигурацию Webpack. Попробуйте изменить порт devServer на 8080. Добавьте новое правило для обработки изображений (файлов с расширением .png, .jpg), используя встроенный лоадер asset/resource. (Подсказка: ищите информацию в официальной документации Webpack).
Пишем код приложения
Давайте создадим простое приложение-калькулятор, чтобы протестировать нашу сборку. Сначала напишем модуль с логикой.
src/modules/calculator.ts
export class Calculator { private currentResult: number; constructor(initialValue: number = 0) { this.currentResult = initialValue; } add(value: number): this { this.currentResult += value; return this; // Возвращаем this для чейнинга (calculator.add(5).multiply(2)) } subtract(value: number): this { this.currentResult -= value; return this; } multiply(value: number): this { this.currentResult *= value; return this; } divide(value: number): this { if (value === 0) { throw new Error("Division by zero is not allowed."); } this.currentResult /= value; return this; } getResult(): number { return this.currentResult; } reset(): void { this.currentResult = 0; } }
Теперь создадим главный файл приложения, который будет использовать наш калькулятор и взаимодействовать с DOM.
src/index.ts
import { Calculator } from '@modules/calculator'; import './styles.css'; // Импортируем стили! // Создаём экземпляр калькулятора const calc = new Calculator(); // Находим элементы DOM const resultDisplay = document.getElementById('result') as HTMLSpanElement; const addButton = document.getElementById('add') as HTMLButtonElement; const subtractButton = document.getElementById('subtract') as HTMLButtonElement; const resetButton = document.getElementById('reset') as HTMLButtonElement; // Функция для обновления отображения результата function updateDisplay(): void { if (resultDisplay) { resultDisplay.textContent = calc.getResult().toString(); } } // Обработчики событий addButton?.addEventListener('click', () => { calc.add(1); updateDisplay(); }); subtractButton?.addEventListener('click', () => { calc.subtract(1); updateDisplay(); }); resetButton?.addEventListener('click', () => { calc.reset(); updateDisplay(); }); // Инициализация отображения updateDisplay(); // Небольшой пример для демонстрации HMR (Hot Module Replacement) console.log('Initial application loaded.');
И, наконец, добавим немного стилей, чтобы наше приложение выглядело прилично.
src/styles.css
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background-color: #f0f0f0; } .calculator { background: white; padding: 2rem; border-radius: 10px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); text-align: center; } .result { font-size: 2.5rem; margin-bottom: 1rem; color: #333; } .buttons { display: flex; gap: 0.5rem; justify-content: center; } button { padding: 0.75rem 1.5rem; font-size: 1rem; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.2s; } button#add { background-color: #4CAF50; color: white; } button#subtract { background-color: #f44336; color: white; } button#reset { background-color: #008CBA; color: white; } button:hover { opacity: 0.9; }
И наш HTML-шаблон, который будет основой для плагина HtmlWebpackPlugin.
public/index.html
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TypeScript Калькулятор</title> </head> <body> <div class="calculator"> <h1>Простой Калькулятор</h1> <div class="result">Результат: <span id="result">0</span></div> <div class="buttons"> <button id="add">+1</button> <button id="subtract">-1</button> <button id="reset">Сброс</button> </div> </div> <!-- Скрипты и стили будут добавлены автоматически HtmlWebpackPlugin --> </body> </html>
Настраиваем скрипты в package.json
Чтобы нам не приходилось каждый раз вводить длинные команды в терминале, мы добавим удобные скрипты в наш package.json.
Откройте package.json и найдите секцию "scripts". Замените её на следующую:
"scripts": { "start": "webpack serve --mode development", "build": "webpack --mode production", "build:dev": "webpack --mode development", "type-check": "tsc --noEmit" }
-
npm start: Запускаетwebpack-dev-serverв режиме разработки. Это наш основной инструмент во время кодинга. -
npm run build: Запускает сборку проекта в продакшен-режиме. Создаёт оптимизированные, минифицированные файлы в папкеdist. -
npm run build:dev: Сборка в режиме разработки (без оптимизаций, для отладки). -
npm run type-check: Запускает компилятор TypeScript только для проверки типов, без генерации файлов. Полезно для CI/CD.
Запуск и тестирование
Всё готово! Давайте запустим наш проект. В терминале, в корне проекта, выполните:
npm start
Через несколько секунд должен автоматически открыться браузер на http://localhost:3000 (или на другом порту, если вы его меняли). Вы увидите ваш калькулятор! Попробуйте понажимать кнопки, всё должно работать. Теперь откройте инструменты разработчика (F12) и перейдите на вкладку «Sources». Вы должны увидеть там ваши оригинальные .ts файлы, благодаря source maps. Это значит, что вы можете отлаживать TypeScript-код прямо в браузере!
Теперь давайте проверим продакшен-сборку. Остановите сервер разработки (Ctrl+C в терминале) и выполните:
npm run build
После завершения процесса загляните в папку dist. Вы увидите там что-то вроде:
-
main.d1b3a8c7f7e92a4d1234.js(ваш минифицированный JS-бандл с хэшем) -
main.d1b3a8c7f7e92a4d1234.css(ваш CSS, вынесенный в отдельный файл) -
index.html(HTML-файл, в который уже автоматически вставлены ссылки на JS и CSS) -
Различные
.js.mapи.css.mapфайлы (source maps для продакшена).
Вы можете открыть файл dist/index.html прямо в браузере и приложение будет работать. Этот комплект файлов из папки dist теперь можно загружать на любой веб-сервер.
Что насчёт Vite?
В мире frontend есть и другие инструменты, которые набирают огромную популярность. Vite один из них. Он создан автором Vue.js и позиционируется как более быстрая альтернатива Webpack, особенно для разработки. Vite использует нативные ES-модули в браузере во время разработки, что избавляет от необходимости сборки всего проекта при каждом изменении. Сборка для продакшена в Vite осуществляется с помощью Rollup.
Настройка Vite для TypeScript-проекта невероятно проста.
-
Установите Vite:
npm install --save-dev vite @vitejs/plugin-react(если используете React) или простоviteдля ванильного JS/TS. -
Создайте файл
vite.config.ts:import { defineConfig } from 'vite'; export default defineConfig({ plugins: [], // Здесь можно добавить плагины, например, для React resolve: { alias: { '@modules': '/src/modules', }, }, server: { port: 3000, }, });
-
Обновите
package.json:"scripts": { "dev": "vite", // Запуск сервера разработки "build": "vite build", // Сборка для продакшена "preview": "vite preview" // Предпросмотр собранного проекта }
-
Убедитесь, что ваш
index.htmlнаходится в корне проекта (а не вpublic) и в него вручную добавлен скрипт:<script type="module" src="/src/index.ts"></script>.
Vite отличный выбор для новых проектов благодаря своей скорости. Webpack же остаётся более зрелым, настраиваемым и до сих пор самым распространённым инструментом, знание которого является must-have для любого фронтенд-разработчика.
Практическая задача 3: Создайте новую ветку в вашем проекте или новый проект и попробуйте настроить сборку с помощью Vite. Сравните скорость запуска сервера разработки и ощущения от HMR с Webpack.
Итоги и заключение
Поздравляю, вы только что прошли практических уроков во всём курсе. Вы построили полноценный конвейер для современной frontend-разработки на TypeScript. Вы теперь умеете:
-
Структурировать проект по общепринятым стандартам.
-
Настраивать TypeScript с помощью
tsconfig.jsonдля строгой и комфортной разработки. -
Настраивать Webpack с лоадерами для TypeScript и CSS, плагинами для HTML и очистки и сервером для разработки.
-
Писать скрипты для автоматизации рутинных задач (сборка, запуск сервера).
-
Понимать разницу между режимами разработки и продакшена.
-
Знать об альтернативах как Vite.
Этот навык, ваш фундамент. С ним вы можете смело начинать работать над реальными проектами, подключать фреймворки вроде React или Vue (для них есть свои лоадеры и плагины), настраивать оптимизации (разделение кода, чанки) и многое другое. Путь изучения TypeScript можно считать успешно пройденным, но помните, что в разработке обучение никогда не заканчивается. Практикуйтесь, создавайте свои проекты, углубляйте знания.
Это был последний, 40-й урок нашего большого курса. Надеюсь, он был для вас полезным и интересным. Все материалы курса, включая этот урок, вы можете найти моём сайте.
Полный курс с уроками по TypeScript для начинающих: https://max-gabov.ru/typescript-dlya-nachinaushih
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


