Special Sponsor:PromptBuilder— Fast, consistent prompt creation powered by 1,000+ expert templates.
Make your Product visible here.Contact Us

Home/TypeScript Strict Mode

TypeScript Strict Mode: What It Does and How to Enable It

A single line in tsconfig.json — but it activates six separate type-checking flags. Here is exactly what each one does and how to adopt them safely in any project.

What Is TypeScript Strict Mode?

TypeScript's default configuration is deliberately permissive to ease adoption. Strict mode tightens the compiler to catch a much wider category of bugs at compile time instead of at runtime. Enable it by adding one line to your tsconfig:

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ES2020", "DOM"],
    "outDir": "./dist",
    "rootDir": "./src",
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

strict: true is equivalent to enabling all six flags listed below individually.

The 6 Flags Enabled by strict: true

1. strictNullChecks

The most impactful flag. Without it, null and undefined are assignable to every type, meaning TypeScript silently allows patterns that crash at runtime.

// strictNullChecks: false (default) — no errors, but crashes at runtime
function getUser(): User | null { return null; }
const user = getUser();
console.log(user.name); // TypeError at runtime!

// strictNullChecks: true — compile-time error forces you to handle null
const user = getUser();
console.log(user.name);         // Error: user is possibly null
console.log(user?.name);        // ✓ optional chaining
if (user) console.log(user.name); // ✓ type guard

// Explicit nullable types
let name: string = null;         // Error with strictNullChecks
let name: string | null = null;  // ✓ must opt-in to nullable

2. noImplicitAny

Without this flag, TypeScript silently assigns any when it cannot infer a type. With it enabled, every untyped parameter and variable must be annotated explicitly.

// noImplicitAny: false — 'data' silently becomes 'any'
function processData(data) {       // implicit any — no error
  return data.value * 2;           // no safety
}

// noImplicitAny: true — must type the parameter
function processData(data: unknown) { // must annotate
  if (typeof data === "object" && data !== null && "value" in data) {
    return (data as { value: number }).value * 2;
  }
}

// Also applies to callbacks
const nums = [1, 2, 3];
nums.forEach((n) => console.log(n));      // ✓ inferred from array type
// If TypeScript can infer, no annotation needed — only unresolvable types error

3. strictFunctionTypes

Enables contravariant checking of function parameter types, preventing unsafe function assignments.

type Handler = (event: MouseEvent) => void;

// Without strictFunctionTypes — this compiles fine (unsafe)
const handler: Handler = (e: Event) => {}; // Event is a supertype of MouseEvent

// With strictFunctionTypes — correct error
// Type '(e: Event) => void' is not assignable to type '(event: MouseEvent) => void'
// MouseEvent has properties that Event doesn't — accessing them would crash

4. strictBindCallApply

Types .bind(), .call(), and .apply() properly, catching wrong argument types.

function add(a: number, b: number): number {
  return a + b;
}

// Without strictBindCallApply — no errors on wrong types
add.call(null, 1, "2");  // no error — "2" is wrong

// With strictBindCallApply — correct error
add.call(null, 1, "2");  // Error: Argument of type 'string' is not assignable to 'number'
add.call(null, 1, 2);    // ✓

5. strictPropertyInitialization

Ensures every class property is assigned in the constructor (or with a field initializer), preventing undefined class instance properties.

// Error — name is declared but never assigned
class User {
  name: string; // Error: Property 'name' has no initializer
}

// Fix 1: Initialize in constructor
class User {
  name: string;
  constructor(name: string) {
    this.name = name; // ✓
  }
}

// Fix 2: Field initializer
class Config {
  host: string = "localhost"; // ✓
  port: number = 3000;        // ✓
}

// Fix 3: Definite assignment assertion — you promise it will be set
class DBService {
  connection!: Connection; // ! tells TypeScript "trust me, it's always set"
  async init() {
    this.connection = await createConnection();
  }
}

6. noImplicitThis

Errors when this in a function would otherwise be typed as any.

// Error — TypeScript cannot infer 'this'
function greet() {
  return `Hello, ${this.name}`; // 'this' implicitly has type 'any'
}

// Fix — use arrow function or annotate 'this'
const obj = {
  name: "Alice",
  greet() {
    return `Hello, ${this.name}`; // ✓ 'this' inferred from object
  },
};

// Or annotate 'this' as a parameter (erased at runtime)
function greet(this: { name: string }) {
  return `Hello, ${this.name}`; // ✓
}

Enabling Strict Mode Incrementally

If your project has hundreds of files, enabling strict: true all at once may produce hundreds of errors. The recommended strategy is to enable flags one at a time.

// Step 1: Start here — catches the most impactful issues
{
  "compilerOptions": {
    "noImplicitAny": true
  }
}

// Step 2: After fixing noImplicitAny errors, add:
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

// Step 3: Continue adding flags until strict: true replaces them all
{
  "compilerOptions": {
    "strict": true  // equivalent to all 6 flags enabled
  }
}

// Per-file opt-out during migration (TypeScript 5.0+)
// @ts-strict-ignore  ← add at top of a file to skip strict for that file

Frequently Asked Questions

What does strict: true do in tsconfig.json?

Setting strict: true enables a group of type-checking flags simultaneously: strictNullChecks, noImplicitAny, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, and noImplicitThis — all at once.

What is strictNullChecks in TypeScript?

With strictNullChecks, null and undefined are not assignable to any other type unless explicitly included in a union. This forces you to handle nullable values explicitly, preventing the most common runtime TypeErrors.

What is noImplicitAny in TypeScript?

noImplicitAny errors when TypeScript cannot infer a type and would fall back to 'any'. Function parameters, uninitialized variables, and callback parameters must all be typed explicitly.

What is strictPropertyInitialization in TypeScript?

strictPropertyInitialization errors when a class property is declared but not initialized in the constructor or with a field initializer. Use the definite assignment assertion (!) for properties set outside the constructor.

How do I enable strict mode incrementally in an existing project?

Enable one flag at a time: start with noImplicitAny, fix all errors, commit, then add strictNullChecks, and so on. Use // @ts-strict-ignore comments on individual files to opt them out temporarily while migrating.

Does strict mode affect JavaScript runtime behavior?

No. TypeScript strict mode is entirely a compile-time feature. It adds more checks during compilation but generates identical JavaScript output.

Migrate your JavaScript to strict TypeScript

Our converter generates TypeScript with proper type annotations — ready to pass strict mode checks without manual work.

Convert now →

From the blog

View all →