Skip to main content

TypeScript tsconfig Configuration

TypeScript's power comes not just from its type system, but also from how configurable it is to suit your project's needs. At the heart of this configurability lies the tsconfig.json file - the command center that tells TypeScript exactly how to handle your code.

What is tsconfig.json?

A tsconfig.json file in your project indicates that the directory is the root of a TypeScript project. This file specifies the compiler options required to compile the project and defines which files should be included or excluded from the compilation process.

Let's start by understanding why you need this file:

  • It provides consistent compiler settings across your team
  • It establishes TypeScript's behavior when integrating with your build tools
  • It enables editor features like IntelliSense and automatic refactoring
  • It controls type-checking strictness and other compilation details

Creating Your First tsconfig.json

You can create a tsconfig.json file by running the TypeScript compiler with the --init flag:

bash
npx tsc --init

This command generates a basic tsconfig.json with commonly used options and helpful comments. The generated file includes many options that are commented out, giving you a reference for future customization.

A minimal tsconfig.json might look like this:

json
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

Essential tsconfig Properties

Let's explore the main sections and properties you'll commonly use in your TypeScript projects:

1. Root Properties

These are the top-level properties in your tsconfig.json:

json
{
"compilerOptions": { /* Compiler-specific options */ },
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"],
"extends": "./base-tsconfig.json",
"files": ["src/main.ts", "src/additional.ts"]
}
  • compilerOptions: Controls how TypeScript compiles your code (we'll dive into this next)
  • include: Specifies files to include using glob patterns
  • exclude: Specifies files to exclude from compilation
  • extends: Path to another configuration file to inherit from
  • files: Explicit list of files to include (useful for smaller projects)

2. Essential Compiler Options

The compilerOptions object contains dozens of settings. Here are the most important ones to understand:

Target JavaScript Version

json
{
"compilerOptions": {
"target": "es2020"
}
}

This option tells TypeScript which ECMAScript version to transpile your code into. Common values include:

  • "es5" - Compatible with older browsers
  • "es6" / "es2015" - For modern environments with ES6 support
  • "es2020", "es2021", etc. - For newer features
  • "esnext" - Latest features

Module System

json
{
"compilerOptions": {
"module": "commonjs"
}
}

This controls what kind of module code TypeScript emits:

  • "commonjs" - For Node.js applications
  • "es2015" / "es6" - ES modules (import/export)
  • "esnext" - For the latest module features
  • "amd", "umd" - For specific module systems

Directory Structure

json
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
}
}

These options define your source and output directory structure:

  • rootDir: Where your TypeScript files live
  • outDir: Where compiled JavaScript files will be placed

Type Checking Strictness

json
{
"compilerOptions": {
"strict": true,
// Or individual flags:
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true
}
}

strict: true enables all strict type-checking options, which helps catch more bugs during development. You can also enable or disable individual strict flags as needed.

Real-World tsconfig Examples

Let's look at some typical configurations for different types of projects:

For a Node.js Backend Project

json
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}

This configuration:

  • Targets Node.js environment with ES2018 features
  • Uses CommonJS modules (standard for Node.js)
  • Enables strict type checking
  • Allows importing JSON files with resolveJsonModule

For a React Frontend Project

json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}

This configuration:

  • Targets older browsers with ES5
  • Includes DOM type definitions
  • Supports JSX with React's newer JSX transform
  • Uses ES modules for tree-shaking in bundlers
  • Sets noEmit because the bundler (like webpack) will handle the actual file output

Understanding Project References

For larger projects with multiple subprojects, TypeScript supports project references:

json
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"composite": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"references": [
{ "path": "../shared-lib" }
]
}

The composite flag enables features needed for project references, while the references array points to other TypeScript projects that this project depends on. This allows for faster, incremental builds of large codebases.

Common Configuration Patterns

Path Aliases

Using path aliases can make imports cleaner and more maintainable:

json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@core/*": ["src/core/*"],
"@utils/*": ["src/utils/*"],
"@components/*": ["src/components/*"]
}
}
}

With this configuration, you can import modules like:

typescript
// Instead of this:
import { Button } from '../../../components/Button';

// You can write this:
import { Button } from '@components/Button';

Source Maps for Debugging

Enable source maps to debug TypeScript code directly:

json
{
"compilerOptions": {
"sourceMap": true
}
}

This generates .js.map files that help debuggers map between your JavaScript and TypeScript code.

Type Definition Configuration

TypeScript's ability to use type definitions from libraries is one of its greatest strengths:

json
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"typeRoots": ["./node_modules/@types", "./src/types"]
}
}
  • declaration generates .d.ts type definition files
  • declarationMap generates source maps for the declaration files
  • typeRoots specifies where to look for type definitions

Advanced tsconfig Features

Conditional Compilation

Use the skipLibCheck option to improve compilation speed by skipping type-checking of declaration files:

json
{
"compilerOptions": {
"skipLibCheck": true
}
}

Incremental Compilation

Speed up build times by enabling incremental compilation:

json
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./buildcache/tsbuildinfo"
}
}

TypeScript will save information about your project's compilation in the specified file and reuse it to speed up future compilations.

Debugging Your tsconfig

If you're experiencing unexpected compilation behavior, you can use TypeScript's --showConfig flag to see the effective configuration after all extends and defaults are applied:

bash
npx tsc --showConfig

This can help identify problems with inheritance or option conflicts.

Practical Example: Setting Up a Full Project

Let's put it all together with a complete example for setting up a Node.js API project:

  1. First, initialize your project and install TypeScript:
bash
mkdir my-ts-api
cd my-ts-api
npm init -y
npm install typescript @types/node --save-dev
  1. Create a baseline tsconfig.json:
bash
npx tsc --init
  1. Modify the tsconfig.json for your API project:
json
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"lib": ["es2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@app/*": ["src/*"],
"@config/*": ["src/config/*"],
"@controllers/*": ["src/controllers/*"],
"@models/*": ["src/models/*"]
},
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
  1. Create your project structure:
my-ts-api/
├── src/
│ ├── config/
│ │ └── database.ts
│ ├── controllers/
│ │ └── userController.ts
│ ├── models/
│ │ └── userModel.ts
│ └── index.ts
├── package.json
└── tsconfig.json
  1. Add a build script to your package.json:
json
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts"
}
}

This setup gives you a well-structured TypeScript project with path aliases, source maps for debugging, and proper module resolution for Node.js.

Common Mistakes and How to Fix Them

Forgetting to Set the Module System

Problem: TypeScript defaults to commonjs modules, which might not be what you want for browser environments.

Solution: Set the appropriate module system:

json
{
"compilerOptions": {
"module": "esnext" // For modern browsers and bundlers
}
}

Incorrect Path Resolution

Problem: Imports fail because TypeScript can't find modules.

Solution: Configure moduleResolution and baseUrl:

json
{
"compilerOptions": {
"moduleResolution": "node",
"baseUrl": "."
}
}

Targeting ES6+ Features but Forgetting Library Definitions

Problem: TypeScript complains about missing definitions for modern JavaScript features.

Solution: Add the appropriate libraries:

json
{
"compilerOptions": {
"target": "es2020",
"lib": ["es2020", "dom"] // Include necessary libraries
}
}

Summary

The tsconfig.json file is the heart of any TypeScript project, providing precise control over how TypeScript processes your code. We've covered:

  • The basic structure and purpose of tsconfig.json
  • Essential compiler options for different project types
  • Real-world configurations for common project scenarios
  • Advanced features like path aliases and project references
  • Practical examples of setting up projects with proper configuration

By understanding and properly configuring your tsconfig.json, you can harness TypeScript's full power while maintaining a smooth developer experience.

Additional Resources

Exercises

  1. Create a tsconfig.json file for a library that needs to support both CommonJS and ES modules.
  2. Set up a tsconfig that supports React with strict type checking.
  3. Configure a monorepo structure with multiple TypeScript projects using project references.
  4. Add path aliases to an existing TypeScript project to simplify import statements.
  5. Optimize a tsconfig for production builds by enabling minification and removing development-only features.


If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)