Menu

TypeScript Strict Mode: What strict: true Turns On

strict: true in tsconfig.json turns on a family of type checks: noImplicitAny, strictNullChecks, strictPropertyInitialization and five more. See what each one catches, how to enable strict mode in an existing project, and the useful flags strict does not include.

This page includes runnable editors - edit, run, and see output instantly.

Strict mode is the "strict": true option in tsconfig.json. It is one switch for eight type-checking flags that catch implicit any, unchecked null and undefined, unsafe function assignments and uninitialized class fields. In TypeScript 7, strict is on by default. This is what code that passes it looks like:

The output is no user, SyntaxError and 42. Remove the : number, the ?. or the instanceof check and the file no longer compiles.

Turning It On

{
    "compilerOptions": {
        "strict": true
    }
}

Individual flags win over strict, in either direction. "strict": true, "strictNullChecks": false keeps everything except null checking; "strict": false, "noImplicitAny": true turns on only that one. strict also opts you into checks added in future releases, since new strict-family flags join the group.

TypeScript's strict has nothing to do with JavaScript's "use strict" directive, which is a run-time mode. TypeScript 7 always emits "use strict" where it is needed, and setting alwaysStrict: false is error TS5108 (the option was removed).

What Each Flag Catches

FlagWhat it reportsError
noImplicitAnya parameter or variable whose type would silently be anyTS7006 Parameter 'x' implicitly has an 'any' type.
strictNullChecksusing a value that may be null or undefinedTS18048 'u' is possibly 'undefined'.
strictFunctionTypesassigning a function whose parameter type is narrower than requiredTS2322
strictBindCallApplywrong arguments to .call, .bind and .applyTS2345
strictPropertyInitializationa class field that is never assignedTS2564 Property 'name' has no initializer and is not definitely assigned in the constructor.
noImplicitThisthis with an implicit any type, as in a nested functionTS2683
useUnknownInCatchVariablesusing a catch variable before narrowing it (it is unknown)TS18046 'err' is of type 'unknown'.
strictBuiltinIteratorReturntreating it.next().value of a built-in iterator as always definedTS2322

The two that change the most code are noImplicitAny and strictNullChecks. This block breaks both on purpose:

It prints index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type. and index.ts(11,13): error TS18048: 'user' is possibly 'undefined'. Without strict, this compiles and then crashes at run time with TypeError: Cannot read properties of undefined (reading 'name'). With strict, the crash becomes a compile error. The fixes are the first block on this page.

strictFunctionTypes: Why It Exists

A function that only handles strings must not be used where numbers can arrive. The check below is silenced with @ts-expect-error so you can run it and see what the error prevents:

Without the comment, the assignment is error TS2322, Type '(s: string) => void' is not assignable to type 'Handler'., followed by Types of parameters 's' and 'value' are incompatible. One exception remains by design: parameters of methods declared with method syntax (handle(value: string | number): void inside an interface) are still checked the looser way, so shout could be assigned to such a method without an error.

strictPropertyInitialization

Every class field must get a value in its declaration or in the constructor. Three ways to satisfy it:

Output:

Account {
  owner: 'Ada',
  balance: 0,
  history: [],
  lastLogin: undefined,
  sessionId: 's-1'
}

The lastLogin and sessionId properties exist with the value undefined because class fields are real JavaScript fields at target ES2022. The ! removes the check without adding any run-time protection, so prefer the other forms. This flag needs strictNullChecks; switching that off disables it too.

Turning On Strict in an Existing Project

Switching a large codebase to strict at once can produce hundreds of errors. Because strict is the TypeScript 7 default, upgrading a project whose tsconfig.json never mentioned strict turns it on by itself; write "strict": false if you need the old behavior while you migrate. A path that keeps the build green:

  1. Add "strict": true and switch off the flags with the most errors, usually "strictNullChecks": false and "noImplicitAny": false.
  2. Fix the remaining errors, then turn on one more flag and repeat.
  3. For noImplicitAny, most fixes are parameter annotations. For strictNullChecks, add | undefined where values can be missing, then handle it with ?., ?? or an if check.
  4. Where a fix has to wait, put // @ts-expect-error with a reason on the line. Unlike @ts-ignore, it reports an error once the problem is gone, so the list shrinks on its own.

Do not reach for as any or ! to silence errors in bulk: each one hides exactly the bug the flag was added to find.

Useful Flags Strict Does Not Include

These are separate because they reject code that is often correct. Many projects turn them on anyway.

FlagWhat it does
noUncheckedIndexedAccessarr[i] and record[key] include undefined in their type
exactOptionalPropertyTypesdebug?: boolean accepts a missing key but not debug: undefined
noImplicitReturnsevery code path of a function with a return value must return (TS7030)
noImplicitOverridea method that overrides a base method must say override (TS4114)
noFallthroughCasesInSwitcha non-empty case must end with break, return or throw (TS7029)
noUnusedLocals, noUnusedParametersunused variables and parameters are errors
noPropertyAccessFromIndexSignaturekeys from an index signature must use obj["key"], not obj.key

noUncheckedIndexedAccess catches the most real bugs. With it on:

const scores = [90, 85];
const d: Record<string, number> = {};

const third: number = scores[2]; // error TS2322: Type 'number | undefined' is not assignable to type 'number'.
const c: number = d["x"];        // same error

const safe = scores[2] ?? 0;     // number
for (const s of scores) {        // for...of is not affected: s is number
    console.log(s);
}

tsc --init in TypeScript 7 turns on noUncheckedIndexedAccess and exactOptionalPropertyTypes in the config it generates, next to strict.

Frequently Asked Questions

What does strict mode do in TypeScript?

"strict": true enables a group of stricter checks: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables and strictBuiltinIteratorReturn. Together they stop any from appearing silently and make null and undefined part of the type system.

Is strict mode on by default in TypeScript?

In TypeScript 7, yes: strict defaults to true, so a project with no strict setting gets every strict check. tsc --init also writes "strict": true explicitly. To opt out you have to write "strict": false.

Can I turn off one strict check but keep the rest?

Yes. Individual flags override strict: { "strict": true, "strictNullChecks": false } keeps every strict check except null checking. This is the usual way to migrate a large codebase one flag at a time.

Is TypeScript strict mode the same as JavaScript "use strict"?

No. "use strict" is a JavaScript run-time mode that changes how code behaves. TypeScript's strict only changes what the type checker reports. TypeScript 7 always emits "use strict" for non-module output, and alwaysStrict: false is now a removed option.

Does strict include noUncheckedIndexedAccess?

No. noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitReturns, noImplicitOverride and noFallthroughCasesInSwitch are separate flags you turn on yourself. tsc --init enables the first two in the config it generates.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED