In Jurassic Park, even the best genetic system is useless without proper calibration of the laboratory equipment. Similarly, TypeScript without proper configuration won't reach its full potential. The
tsconfig.json file is your control panel — it defines how the TypeScript compiler interprets and transforms your code.Every TypeScript project starts with a
tsconfig.json file in the root directory. This is a JSON file defining compiler options, which files should be compiled, and how the output should look:1{
2 "compilerOptions": {
3 "target": "ES2020",
4 "module": "ESNext",
5 "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 "outDir": "./dist",
7 "rootDir": "./src",
8 "strict": true,
9 "esModuleInterop": true,
10 "skipLibCheck": true,
11 "forceConsistentCasingInFileNames": true
12 },
13 "include": ["src/**/*"],
14 "exclude": ["node_modules", "dist"]
15}Target — output JavaScript version:
ES5 — compatibility with older browsersES2020 — modern environments (nullish coalescing, optional chaining)ESNext — latest featuresModule — module system:
CommonJS — Node.js (require/module.exports)ESNext — ES modules (import/export)NodeNext — Node.js with ESM supportLib — which APIs are available:
DOM — browser API (document, window)ES2020 — methods like Promise.allSettled, BigIntWebWorker — Web Workers APIThe
strict: true flag enables all rigorous checks at once. It's like turning on full security in Jurassic Park — no dinosaur will escape the enclosure:1// strict: true enables ALL of the following:
2{
3 "strictNullChecks": true, // null/undefined must be handled
4 "strictFunctionTypes": true, // strict typing of function parameters
5 "strictBindCallApply": true, // strict typing of bind/call/apply
6 "strictPropertyInitialization": true, // properties must be initialized
7 "noImplicitAny": true, // implicit any is forbidden
8 "noImplicitThis": true, // this must have a type
9 "alwaysStrict": true, // "use strict" in every file
10 "useUnknownInCatchVariables": true // catch(e) is unknown, not any
11}Example difference with
strictNullChecks:1// WITHOUT strictNullChecks — runtime error!
2function getDinoName(id: string): string {
3 const dino = dinosaurs.find(d => d.id === id);
4 return dino.name; // Runtime error: Cannot read property 'name' of undefined
5}
6
7// WITH strictNullChecks — TypeScript enforces null handling
8function getDinoName(id: string): string | undefined {
9 const dino = dinosaurs.find(d => d.id === id);
10 return dino?.name; // Safe — returns undefined if not found
11}In large projects, imports can become unreadable. Path aliases allow you to create shortcuts:
1{
2 "compilerOptions": {
3 "baseUrl": ".",
4 "paths": {
5 "@models/*": ["src/models/*"],
6 "@utils/*": ["src/utils/*"],
7 "@services/*": ["src/services/*"],
8 "@config": ["src/config/index.ts"]
9 }
10 }
11}Instead of:
1import { Dinosaur } from '../../../models/dinosaur';
2import { formatDate } from '../../utils/helpers';You write:
1import { Dinosaur } from '@models/dinosaur';
2import { formatDate } from '@utils/helpers';Declaration files describe types for JavaScript code. They have the
.d.ts extension and contain no implementation — only type signatures:1// dinosaur.d.ts — type declaration
2declare interface IDinosaur {
3 id: string;
4 name: string;
5 species: string;
6 dangerLevel: 1 | 2 | 3 | 4 | 5;
7}
8
9declare function findDinosaur(id: string): IDinosaur | null;
10declare const PARK_NAME: string;
11
12// Module declaration for a JS library without types
13declare module 'dino-tracker' {
14 export function trackPosition(id: string): [number, number];
15 export function getStatus(id: string): 'active' | 'sleeping' | 'escaped';
16}Three types of declaration files:
declaration: true flagMany JavaScript libraries don't have built-in types. The DefinitelyTyped repository contains community-created types:
1# Installing types for popular libraries
2npm install --save-dev @types/node
3npm install --save-dev @types/express
4npm install --save-dev @types/lodash
5npm install --save-dev @types/jestTypeScript automatically recognizes
@types/* packages — you don't need to import them. If a library has built-in types (like axios or date-fns), you don't need a separate @types package.The
typeRoots option in tsconfig controls where TypeScript loads types from:1{
2 "compilerOptions": {
3 "typeRoots": ["./node_modules/@types", "./src/types"]
4 }
5}TypeScript has its own type checks, but ESLint with the
@typescript-eslint plugin adds stylistic rules and advanced analysis:1{
2 "parser": "@typescript-eslint/parser",
3 "plugins": ["@typescript-eslint"],
4 "extends": [
5 "eslint:recommended",
6 "plugin:@typescript-eslint/recommended",
7 "plugin:@typescript-eslint/recommended-requiring-type-checking"
8 ],
9 "parserOptions": {
10 "project": "./tsconfig.json"
11 },
12 "rules": {
13 "@typescript-eslint/no-explicit-any": "warn",
14 "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
15 "@typescript-eslint/prefer-nullish-coalescing": "error",
16 "@typescript-eslint/prefer-optional-chain": "error"
17 }
18}Most important
@typescript-eslint rules:no-explicit-any — warns about using anyno-unused-vars — detects unused variables (with TypeScript support)prefer-nullish-coalescing — prefer ?? instead of ||prefer-optional-chain — prefer a?.b instead of a && a.bstrict-boolean-expressions — forbids implicit boolean conversionsAdditional compiler flags to help maintain code quality:
1{
2 "compilerOptions": {
3 "noUnusedLocals": true, // error on unused variables
4 "noUnusedParameters": true, // error on unused parameters
5 "noImplicitReturns": true, // every path must return a value
6 "noFallthroughCasesInSwitch": true, // enforces break in switch
7 "noUncheckedIndexedAccess": true, // arr[i] is T | undefined
8 "exactOptionalPropertyTypes": true // undefined !== omitting a field
9 }
10}The
noUncheckedIndexedAccess option is particularly useful — it protects against indexing errors:1const dinos = ["T-Rex", "Raptor", "Triceratops"];
2
3// WITHOUT noUncheckedIndexedAccess
4const first: string = dinos[0]; // OK, but dinos[100] would also be "string"!
5
6// WITH noUncheckedIndexedAccess
7const first: string | undefined = dinos[0]; // TypeScript enforces checking
8if (first) {
9 console.log(first.toUpperCase()); // Safe
10}