WritingTypeScript Strict Mode: The tsconfig Flags That Actually Matter — Clixo
5 min readtypescript, strict-mode, tsconfig, configuration, type-safety

TypeScript Strict Mode: The tsconfig Flags That Actually Matter

A practical breakdown of TypeScript strict mode — what each flag does, which ones strict enables, and what strict misses that you should add manually.

"strict": true is the most common TypeScript configuration advice. Enable it, and TypeScript helps you more. But what does strict actually do, which bugs does it catch, and what does it miss? Most developers can answer the first question vaguely and not at all for the last two.

This guide breaks down each flag that strict enables and covers what strict does not enable — where you need to add flags explicitly to close real gaps.

What TypeScript Strict Mode Enables

strict: true in your tsconfig.json is shorthand for eight specific compiler flags. Enabling strict is equivalent to setting all of them individually.

strictNullChecks

The most impactful flag. Without it, null and undefined are assignable to every type. With it, they are their own distinct types:

// Without strictNullChecks
let name: string = null; // allowed
 
// With strictNullChecks
let name: string = null; // Error: Type 'null' is not assignable to type 'string'
let name: string | null = null; // correct

This catches the largest class of runtime errors — accessing a property on something that turned out to be null or undefined. If you only enable one flag, make it this one. strict: true includes it.

noImplicitAny

Without this flag, TypeScript silently infers any when it cannot determine a type — particularly for function parameters:

// Without noImplicitAny — TypeScript infers any for 'user'
function log(user) {
  console.log(user.name); // no error even if name does not exist
}
 
// With noImplicitAny
function log(user: User) { // must annotate explicitly
  console.log(user.name);
}

any disables type checking for that value. Implicit any is TypeScript silently giving up without telling you. This flag makes silence impossible — TypeScript either infers a real type or requires an explicit annotation.

strictFunctionTypes

Makes function parameter types checked contravariantly. Without this, you can pass a function expecting a Dog where a function expecting an Animal is required, which allows type-unsafe code. With it, the check is strict in both directions.

This flag primarily affects higher-order functions and callbacks. Most developers do not notice it directly, but it closes a real soundness hole.

strictBindCallApply

Makes bind, call, and apply type-checked. Without it, these methods accept any arguments and return any. With it, TypeScript verifies that the arguments you pass to fn.call(thisArg, arg1, arg2) match fn's parameter types.

strictPropertyInitialization

Requires that class properties are either initialized in the declaration or assigned in the constructor:

class UserService {
  private db: Database; // Error: Property 'db' has no initializer
 
  // Fix: assign in constructor
  constructor(db: Database) {
    this.db = db;
  }
}

This catches the common pattern of declaring a class property but forgetting to initialize it, which would cause a runtime error the first time the property is accessed.

noImplicitThis

Requires explicit typing of this in functions where TypeScript cannot determine its type automatically. Relevant mostly in class methods, event handlers, and functions passed as callbacks.

alwaysStrict

Emits "use strict" at the top of every compiled JavaScript file. This is largely cosmetic in modern Node.js (which runs in strict mode by default) but ensures correct behavior in older environments.

useUnknownInCatchVariables

Since TypeScript 4.4, catch clause variables default to unknown with this flag instead of any. This is correct — you do not know what was thrown:

try {
  riskyOperation();
} catch (err) {
  // With this flag: err is unknown
  // Without: err is any (TypeScript lets you do anything with it)
  if (err instanceof Error) {
    console.error(err.message);
  }
}

TypeScript Strict Mode: What It Misses

strict: true does not cover everything. These flags provide meaningful additional safety and need to be added separately.

noUncheckedIndexedAccess

The most impactful flag not in strict. Without it, array index access returns T, not T | undefined:

const items: string[] = [];
const first = items[0]; // string — TypeScript lies
first.toUpperCase();    // runtime crash
 
// With noUncheckedIndexedAccess:
const first = items[0]; // string | undefined — TypeScript is honest

This catches a very common class of runtime crash. The fix is usually a nullish coalescing or an explicit guard. Enable it, fix the errors, ship with confidence.

noImplicitReturns

Catches functions that fall through without returning a value in some branches:

function getLabel(status: string): string {
  if (status === "active") return "Active";
  // forgot to handle other cases — implicit return is undefined
}

With noImplicitReturns, the function above is a compile error. Add it.

exactOptionalPropertyTypes

Without this flag, setting an optional property to undefined is the same as having it present with an undefined value — which is semantically different from omitting it:

interface Config {
  timeout?: number;
}
 
const cfg: Config = { timeout: undefined }; // allowed without exactOptionalPropertyTypes

With it, the above is an error. You must either omit timeout or set it to a number. This matters when serializing to JSON or when the receiving code checks "timeout" in cfg.

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "exactOptionalPropertyTypes": true,
    "noFallthroughCasesInSwitch": true
  }
}

Start here. These flags together close the most common gaps between "TypeScript says it is fine" and "this crashes in production."


If your team wants to get TypeScript configuration right from the start — or tighten up an existing codebase — Clixo can audit your setup and help you ship with higher confidence.