TypeScript Tooling
This chapter is a tour of the tools that surround TypeScript: linting, formatting, build tools, editor setup and testing.
TypeScript Development Ecosystem
TypeScript's tooling is one of its biggest strengths. There are strong tools for every stage of development:
- Code quality: ESLint with TypeScript support, type checking and linting, code style enforcement.
- Development: VS Code integration, debugging tools, Hot Module Replacement (HMR).
- Build & deploy: bundlers (Vite, Webpack, Parcel), module bundling, production optimisation.
Linting with ESLint
Installation. Install ESLint together with the official TypeScript parser and plugin, so ESLint understands TypeScript syntax and rules: npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin.
Configuration. In .eslintrc.json, turn on the recommended TypeScript rules, point ESLint at your tsconfig for type-aware linting, and adjust a few common rules:
"root": true"parser": "@typescript-eslint/parser""plugins": ["@typescript-eslint"]"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking"]"parserOptions": { "project": "./tsconfig.json", "ecmaVersion": 2020, "sourceType": "module" }"rules": { "@typescript-eslint/explicit-function-return-type": "warn", "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] }
NPM scripts. Add scripts to package.json that run the linter and a type-only check. Use lint:fix to auto-fix simple problems.
"lint": "eslint . --ext .ts,.tsx""lint:fix": "eslint . --ext .ts,.tsx --fix""type-check": "tsc --noEmit"
Code Formatting with Prettier
Prettier keeps the code style the same across your whole team.
Combine it with ESLint so formatting problems do not show up as lint errors.
Installation. Install Prettier plus the ESLint packages that turn off conflicting rules and report formatting issues through ESLint: npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier.
Configuration. Put your preferences in .prettierrc, for example:
"semi": true"singleQuote": true"tabWidth": 2"printWidth": 100"trailingComma": "es5""bracketSpacing": true"arrowParens": "avoid"
And list generated folders in .prettierignore, one per line: node_modules, build, dist, .next, .vscode.
Integrate with ESLint. Extend plugin:prettier/recommended so formatting problems are reported as ESLint issues. In .eslintrc.json add it to the "extends" array, after all other configs: "extends": ["plugin:prettier/recommended"]. It must be the last entry in the array.
Modern Build Tools
Vite is the recommended choice for a fast dev server and modern builds.
Webpack is still common and very configurable.
Vite (recommended). Create a new React + TypeScript project with Vite. It starts a dev server with HMR for quick feedback:
npm create vite@latest my-app -- --template react-ts— create the projectcd my-app— go into the project foldernpm install— install dependenciesnpm run dev— start the development server
Webpack configuration. If you pick Webpack, this minimal setup compiles TypeScript, handles CSS and serves the app with webpack-dev-server.
TypeScript configuration. A strict tsconfig.json for modern browsers. The optional baseUrl and paths allow absolute imports such as @/components/Button. Its compilerOptions:
"target": "es2020""module": "esnext""lib": ["dom", "dom.iterable", "esnext"]"allowJs": true"skipLibCheck": true"esModuleInterop": true"allowSyntheticDefaultImports": true"strict": true"forceConsistentCasingInFileNames": true"moduleResolution": "node""resolveJsonModule": true"isolatedModules": true"noEmit": true"jsx": "react-jsx""baseUrl": ".""paths": { "@/*": ["src/*"] }
with "include": ["src"] and "exclude": ["node_modules"].
Minimal Webpack setup
ts-loader handles .ts/.tsx files, CSS goes through the style and css loaders, and the dev server runs on port 3000 with hot reloading.
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
devServer: {
static: path.join(__dirname, 'dist'),
compress: true,
port: 3000,
hot: true,
},
};
Development Environment Setup
Set up your editor so it shows lint errors, formats on save and debugs easily.
VS Code extensions. Recommended extensions:
- TypeScript + Webpack Problem Matchers — better error reporting
- ESLint — runs ESLint inside VS Code
- Prettier - Code formatter — consistent formatting
- Path IntelliSense — autocompletes file names
- Error Lens — shows errors inline
VS Code settings. In .vscode/settings.json, make Prettier the default formatter, fix ESLint issues on save and prefer non-relative import paths:
"editor.defaultFormatter": "esbenp.prettier-vscode""editor.formatOnSave": true"editor.codeActionsOnSave": { "source.fixAll.eslint": true, "source.organizeImports": true }"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"]"typescript.tsdk": "node_modules/typescript/lib""typescript.preferences.importModuleSpecifier": "non-relative"
Debugging configuration. In .vscode/launch.json ("version": "0.2.0"), add two entries to "configurations":
- A Chrome launch:
"type": "chrome","request": "launch","name": "Launch Chrome against localhost","url": "http://localhost:3000","webRoot": "${workspaceFolder}","sourceMaps": true, and"sourceMapPathOverrides"mappingwebpack:///./~/*to${workspaceFolder}/node_modules/*andwebpack:///./*to${workspaceFolder}/src/*. - A Node test debugger:
"type": "node","request": "launch","name": "Debug Tests","runtimeExecutable": "${workspaceRoot}/node_modules/.bin/jest","args": ["--runInBand", "--watchAll=false"],"console": "integratedTerminal","internalConsoleOptions": "neverOpen","sourceMaps": true.
Testing Setup
Jest with Testing Library is a popular way to test React + TypeScript apps.
Jest + Testing Library. Install Jest, its TypeScript support and the React Testing Library helpers for DOM assertions and user interaction: npm install --save-dev jest @types/jest ts-jest @testing-library/react @testing-library/jest-dom @testing-library/user-event.
Jest configuration. Configure Jest to use ts-jest, map CSS modules, and resolve alias paths such as @/.
Example test. A simple component test that checks rendering and click behaviour with Testing Library.
Jest configuration
The config uses the ts-jest preset with a jsdom environment and maps the @/ alias and style imports.
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['@testing-library/jest-dom'],
moduleNameMapper: {
'^@/(.*)$': '/src/$1',
'\\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
transform: {
'^.+\\\.tsx?$': 'ts-jest',
},
testMatch: ['**/__tests__/**/*.test.(ts|tsx)'],
};
Component test with Testing Library
Two tests: one checks the button text is rendered, the other checks that the click handler runs once.
// src/__tests__/Button.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Button from '../components/Button';
describe('Button', () => {
it('renders button with correct text', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
Best Practices
Development workflow:
- Use
npm run devfor development with hot reloading - Run
npm run type-checkto verify TypeScript types - Use
npm run lintto check for linting errors - Run
npm run buildto create a production build
Performance optimisation:
- Use code splitting with dynamic imports
- Enable tree-shaking in production builds
- Use
React.memoanduseMemofor expensive computations - Lazy load non-critical components
Common Pitfalls
- TypeScript configuration: make sure
strictmode is enabled - ESLint + Prettier conflicts: use
eslint-config-prettierto disable the conflicting rules - Slow builds: consider Vite or esbuild for faster development
- Missing type definitions: install
@typespackages for all dependencies - Debugging issues: make sure source maps are configured correctly
Recommended Tools
- Bundlers: Vite, Webpack, Parcel
- Testing: Jest, React Testing Library, Cypress
- Linting/Formatting: ESLint, Prettier, Stylelint
- Documentation: TypeDoc, Storybook
- Performance: Web Vitals, Lighthouse