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
| Flag | What it reports | Error |
|---|---|---|
noImplicitAny | a parameter or variable whose type would silently be any | TS7006 Parameter 'x' implicitly has an 'any' type. |
strictNullChecks | using a value that may be null or undefined | TS18048 'u' is possibly 'undefined'. |
strictFunctionTypes | assigning a function whose parameter type is narrower than required | TS2322 |
strictBindCallApply | wrong arguments to .call, .bind and .apply | TS2345 |
strictPropertyInitialization | a class field that is never assigned | TS2564 Property 'name' has no initializer and is not definitely assigned in the constructor. |
noImplicitThis | this with an implicit any type, as in a nested function | TS2683 |
useUnknownInCatchVariables | using a catch variable before narrowing it (it is unknown) | TS18046 'err' is of type 'unknown'. |
strictBuiltinIteratorReturn | treating it.next().value of a built-in iterator as always defined | TS2322 |
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:
- Add
"strict": trueand switch off the flags with the most errors, usually"strictNullChecks": falseand"noImplicitAny": false. - Fix the remaining errors, then turn on one more flag and repeat.
- For
noImplicitAny, most fixes are parameter annotations. ForstrictNullChecks, add| undefinedwhere values can be missing, then handle it with?.,??or anifcheck. - Where a fix has to wait, put
// @ts-expect-errorwith 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.
| Flag | What it does |
|---|---|
noUncheckedIndexedAccess | arr[i] and record[key] include undefined in their type |
exactOptionalPropertyTypes | debug?: boolean accepts a missing key but not debug: undefined |
noImplicitReturns | every code path of a function with a return value must return (TS7030) |
noImplicitOverride | a method that overrides a base method must say override (TS4114) |
noFallthroughCasesInSwitch | a non-empty case must end with break, return or throw (TS7029) |
noUnusedLocals, noUnusedParameters | unused variables and parameters are errors |
noPropertyAccessFromIndexSignature | keys 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.