Связка babel-loader и
@babel/preset-typescript используется для обработки
TypeScript-кода внутри сборочного процесса Webpack без применения
компилятора TypeScript (tsc) как основного
транспайлера.
babel-loader подключает Babel к пайплайну Webpack, а
@babel/preset-typescript добавляет поддержку синтаксиса
TypeScript. В результате Babel может:
Типичная схема обработки:
TypeScript → Babel → Webpack bundle
В отличие от ts-loader, Babel не занимается полноценной
компиляцией TypeScript. Он работает исключительно как транспайлер
синтаксиса.
Минимальный набор пакетов:
npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-typescript
Для React-проектов дополнительно:
npm install -D @babel/preset-react
Для поддержки современных возможностей Jav * aScript:
npm install -D core-js
Пример webpack.config.js:
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
};
Пример .babelrc:
{
"presets": [
"@babel/preset-env",
"@babel/preset-typescript"
]
}
Либо babel.config.js:
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-typescript',
],
};
@babel/preset-typescriptPreset не выполняет полноценную компиляцию TypeScript. Его задача — удалить типовую информацию и оставить валидный JavaScript.
Исходный код:
const sum = (a: number, b: number): number => {
return a + b;
};
После Babel:
const sum = (a, b) => {
return a + b;
};
Типы полностью исчезают.
.ts и
.tsxДля React-проектов:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}
Webpack:
resolve: {
extensions: ['.tsx', '.ts', '.js'],
}
Пример компонента:
type Props = {
title: string;
};
export const Header = ({ title }: Props) => {
return <h1>{title}</h1>;
};
Babel:
Babel значительно быстрее ts-loader в больших проектах,
особенно при использовании кеширования.
Причины:
Для крупных frontend-проектов это критично.
Babel предоставляет огромное количество плагинов:
Пример:
{
"plugins": [
"@babel/plugin-proposal-decorators"
]
}
@babel/preset-env позволяет адаптировать код под
конкретные браузеры.
Пример:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "> 0.25%, not dead"
}
]
]
}
Babel является стандартом де-факто для React-инфраструктуры.
Поддерживаются:
Babel одинаково удобно обрабатывает:
.js.jsx.ts.tsxЭто удобно при постепенной миграции проекта.
Связка отлично работает с:
Главная проблема @babel/preset-typescript — отсутствие
проверки типов.
Пример:
const value: number = 'hello';
Babel успешно соберёт проект.
Ошибки не будет.
Проблема проявится только:
tsc;Babel работает по принципу:
Один файл → преобразование → результат
TypeScript-компилятор анализирует:
Babel этого не делает.
fork-ts-checker-webpack-pluginДля полноценной проверки типов обычно подключают отдельный плагин:
npm install -D fork-ts-checker-webpack-plugin typescript
Настройка:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
plugins: [
new ForkTsCheckerWebpackPlugin(),
],
};
Теперь:
Это наиболее распространённая production-схема.
tsconfig.jsonДаже при использовании Babel файл tsconfig.json остаётся
важным.
Пример:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"jsx": "react-jsx",
"isolatedModules": true
}
}
isolatedModulesПри использовании Babel рекомендуется:
{
"isolatedModules": true
}
Эта опция запрещает конструкции TypeScript, требующие анализа нескольких файлов.
Причина в том, что Babel компилирует файлы независимо друг от друга.
tsc.d.tsBabel не умеет создавать declaration files.
Для библиотек это серьёзное ограничение.
Необходим отдельный запуск:
tsc --emitDeclarationOnly
TypeScript умеет использовать информацию о типах при компиляции.
Babel — нет.
Некоторые конструкции работают иначе или не поддерживаются.
const enumПример:
const enum Roles {
Admin,
User
}
TypeScript может inline-подставлять значения enum.
Babel historically имеет ограничения с const enum.
Часто рекомендуется:
{
"preserveConstEnums": true
}
или отказ от const enum.
TypeScript namespace:
namespace Utils {
export const value = 10;
}
Babel поддерживает namespace ограниченно и не рекомендует их использование.
Современный подход — ES Modules.
Decorators в Babel и TypeScript отличаются.
Особенно:
Конфигурация может быть сложной:
{
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}
@babel/preset-envНаиболее типичная схема:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
],
"@babel/preset-typescript"
]
}
Возможности:
ts-loaderts-loaderИспользует настоящий TypeScript compiler API.
Особенности:
babel-loaderОсобенности:
babel-loaderОсобенно:
При тысячах модулей скорость сборки становится критичной.
Например:
Babel проще масштабируется в больших frontend-монорепозиториях.
ts-loader лучшеОсобенно при необходимости:
.d.ts;Например:
Там важнее строгая типизация, чем скорость UI-сборки.
Когда критичны:
transpileOnlyУ ts-loader существует режим:
{
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
По поведению он близок к Babel:
Но Babel всё равно остаётся быстрее и гибче.
Babel поддерживает source maps через Webpack.
Пример:
module.exports = {
devtool: 'source-map',
};
Это важно для:
Для ускорения:
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
Babel сохраняет промежуточные результаты на диск.
Повторные сборки становятся значительно быстрее.
thread-loaderДополнительная оптимизация:
{
test: /\.tsx?$/,
use: [
'thread-loader',
'babel-loader'
]
}
Преимущества:
Недостаток — дополнительные накладные расходы на запуск worker-процессов.
Babel легко интегрируется с Jest.
Пример:
npm install -D babel-jest
Конфигурация:
module.exports = {
transform: {
'^.+\\.tsx?$': 'babel-jest',
},
};
Babel отлично совместим с hot reload.
Пример:
npm install -D react-refresh @pmmmwh/react-refresh-webpack-plugin
Это одна из причин популярности Babel в React-экосистеме.
webpack.config.jsconst path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true,
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
},
],
},
plugins: [
new ForkTsCheckerWebpackPlugin(),
],
};
babel.config.jsmodule.exports = {
presets: [
[
'@babel/preset-env',
{
targets: '> 0.25%, not dead',
useBuiltIns: 'usage',
corejs: 3,
},
],
[
'@babel/preset-react',
{
runtime: 'automatic',
},
],
'@babel/preset-typescript',
],
};
tsconfig.json{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"jsx": "react-jsx",
"isolatedModules": true,
"noEmit": true
}
}
Наиболее распространённая современная схема:
TypeScript → Babel → Webpack → Bundle
↓
ForkTsChecker
Разделение ответственности:
| Инструмент | Задача |
|---|---|
| Babel | Трансформация кода |
| Webpack | Сборка модулей |
| TypeScript | Проверка типов |
| ForkTsChecker | Асинхронная диагностика |
При использовании Babel большая часть типовых ошибок обнаруживается:
tscТипичная команда:
tsc --noEmit
Это гарантирует отсутствие типовых ошибок перед деплоем.
Babel понимает синтаксис TypeScript, но не реализует всю модель TypeScript-компилятора.
Это принципиально разные инструменты.
Babel хорошо справляется с:
Но сложные compile-time возможности TypeScript могут вести себя иначе.
В больших frontend-системах различие особенно заметно:
| Инструмент | Скорость |
|---|---|
| ts-loader | Ниже |
| babel-loader | Выше |
Особенно:
Связка babel-loader +
@babel/preset-typescript стала фактическим стандартом
для:
Основная причина — баланс между: