Webpack provides excellent support for TypeScript through loaders and plugins, enabling type-safe development with full build optimization.
Basic Setup
Install dependencies
npm install --save-dev typescript ts-loader
Create tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"jsx": "react",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"sourceMap": true,
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Configure webpack
module.exports = {
entry: './src/index.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
}
};
Add build script
In package.json:{
"scripts": {
"build": "webpack",
"type-check": "tsc --noEmit"
}
}
Set "module": "ESNext" in tsconfig.json to preserve ES modules for webpack’s tree shaking.
ts-loader
The standard TypeScript loader for webpack:
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
// Faster builds in development
transpileOnly: true,
// Use specific tsconfig
configFile: 'tsconfig.build.json'
}
}
]
}
};
transpileOnly Mode
Skip type checking for faster builds:
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
]
}
};
With transpileOnly: true, type errors won’t stop the build. Run tsc --noEmit separately to check types.
Fork TS Checker Plugin
Run type checking in a separate process for faster builds:
Install plugin
npm install --save-dev fork-ts-checker-webpack-plugin
Configure webpack
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
]
},
plugins: [
new ForkTsCheckerWebpackPlugin({
async: true // Don't block webpack in development
})
]
};
This is the recommended setup for development - it provides fast rebuilds while still showing type errors.
Babel with TypeScript
Use Babel for transpilation and TypeScript only for type checking:
Install dependencies
npm install --save-dev @babel/core @babel/preset-env @babel/preset-typescript babel-loader
Create babel.config.js
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
'@babel/preset-typescript'
]
};
Configure webpack
module.exports = {
module: {
rules: [
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx']
}
};
Babel is faster than ts-loader but doesn’t type check. Use with ForkTsCheckerWebpackPlugin or run tsc --noEmit separately.
Development Configuration
Optimize for fast development builds:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'eval-source-map',
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
transpileOnly: true // Fast builds
}
}
]
},
plugins: [
new ForkTsCheckerWebpackPlugin({
async: true, // Non-blocking
typescript: {
diagnosticOptions: {
semantic: true,
syntactic: true
}
}
})
],
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
cache: {
type: 'filesystem'
}
};
Production Configuration
Optimize for production builds:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'source-map',
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
transpileOnly: false // Full type checking
}
}
]
},
plugins: [
new ForkTsCheckerWebpackPlugin({
async: false // Block on errors
})
],
resolve: {
extensions: ['.tsx', '.ts', '.js']
}
};
Path Mapping
Use TypeScript path aliases:
tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
}
}
}
webpack.config.js:
const path = require('path');
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/'),
'@components': path.resolve(__dirname, 'src/components/'),
'@utils': path.resolve(__dirname, 'src/utils/')
},
extensions: ['.tsx', '.ts', '.js']
}
};
Install tsconfig-paths-webpack-plugin to automatically sync webpack aliases with tsconfig paths.
React with TypeScript
npm install --save react react-dom
npm install --save-dev @types/react @types/react-dom
tsconfig.json:
{
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["ES2020", "DOM", "DOM.Iterable"]
}
}
webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
}
};
Type Declarations
For Webpack Assets
Create src/types/assets.d.ts:
declare module '*.png' {
const value: string;
export default value;
}
declare module '*.jpg' {
const value: string;
export default value;
}
declare module '*.svg' {
const value: string;
export default value;
}
declare module '*.css' {
const classes: { [key: string]: string };
export default classes;
}
For Custom Modules
declare module 'my-untyped-package' {
export function doSomething(value: string): void;
}
Environment-Specific Configs
Use different tsconfig for different environments:
tsconfig.json (base):
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"strict": true
}
}
tsconfig.build.json:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": true
},
"include": ["src"],
"exclude": ["**/*.test.ts"]
}
webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
configFile: 'tsconfig.build.json'
}
}
]
}
};
Project References
For monorepos with multiple TypeScript projects:
tsconfig.base.json:
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true
}
}
packages/app/tsconfig.json:
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"references": [
{ "path": "../shared" }
]
}
Common Issues
Issue: Slow Builds
Solution: Use transpileOnly with ForkTsCheckerWebpackPlugin:
{
loader: 'ts-loader',
options: { transpileOnly: true }
}
Issue: Type Errors Not Showing
Solution: Ensure type checking is enabled:
new ForkTsCheckerWebpackPlugin({
async: false // Block on errors in production
})
Issue: Module Resolution Errors
Solution: Check resolve extensions:
resolve: {
extensions: ['.tsx', '.ts', '.js', '.json']
}
Always include .js in extensions array, as some dependencies may use JavaScript files.
Best Practices
Use transpileOnly in development
Speed up builds and use ForkTsCheckerWebpackPlugin for type checking
Enable strict mode
{ "compilerOptions": { "strict": true } }
Use ESNext modules
Preserve ES modules for better tree shaking:{ "compilerOptions": { "module": "ESNext" } }
Type your webpack config
import type { Configuration } from 'webpack';
const config: Configuration = {
// ...
};