Strict Mode in TypeScript
Strict mode enables every type-safety check TypeScript offers. Learn what strict: true does, which checks it enables, and why you should use it from day one.
Setting strict to true in your tsconfig.json is a single switch that enables all of TypeScript's strict type-checking flags at once. It is the most important setting in your configuration and the one that most directly affects how many bugs TypeScript catches before your code runs.
When strict is enabled, TypeScript turns on nine individual checks. Each one catches a specific category of problem. Together they form a safety net that catches null reference errors, uninitialized properties, implicit any types, incorrect function assignments, and more.
The nine checks strict mode enables
Here is what strict: true turns on:
| Flag | What it catches | Example bug prevented |
|---|---|---|
| strictNullChecks | Using null or undefined where a real value is expected | Accessing .name on a find() result that might be undefined |
| noImplicitAny | Variables and parameters the compiler cannot type | A function parameter with no type annotation |
| strictFunctionTypes | Assigning a function where parameter types do not match safely | Passing a string-only callback where string-or-number is expected |
| strictBindCallApply | Wrong argument types to .call(), .bind(), .apply() | Calling fn.call(undefined, false) when fn expects a string |
| strictPropertyInitialization | Class properties declared but not set in the constructor | A name field with no initializer and no constructor assignment |
| noImplicitThis | this expressions where the type cannot be determined | Using this inside a regular function callback |
| alwaysStrict | Files not parsed in ECMAScript strict mode | Adds "use strict" to every emitted file |
| strictBuiltinIteratorReturn | Built-in iterator .next() results typed as any after done | Reading a property off a finished iterator's value without a type error |
| useUnknownInCatchVariables | Catch clause variables defaulting to any | catch (err) where err is any instead of unknown |
Future TypeScript versions may add new checks under the strict umbrella. Your code that compiles with strict today might get new errors after a TypeScript upgrade. You can disable a newly added check individually if needed.
The three most impactful checks
strictNullChecks
Without strictNullChecks, null and undefined are assignable to any type. This silently hides a huge class of bugs. Consider finding a user in an array:
const users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
];
const loggedInUser = users.find((u) => u.name === "Charlie");
console.log(loggedInUser.age);With strictNullChecks off, this compiles fine but crashes at runtime because .find() returns undefined when no match is found.
With strictNullChecks on, you get a clear compile error before the code ever runs:
'loggedInUser' is possibly 'undefined'.You are forced to handle the missing case, which prevents the runtime crash entirely. For more on this topic, see null and undefined in TypeScript.
noImplicitAny
When TypeScript cannot infer a type, it falls back to any. With noImplicitAny on, the compiler requires an explicit annotation instead of silently accepting the unsafe fallback. This is covered in depth in noImplicitAny in TypeScript.
strictPropertyInitialization
This check ensures that every declared class property is definitely assigned before use. It catches a common mistake where a property is declared but forgotten in the constructor:
class User {
name: string;
email: string;
isAdmin = false;
constructor(name: string) {
this.name = name;
// email is never set
}
}With strictPropertyInitialization on, the compiler reports that email has no initializer and is not definitely assigned. The name property is fine because it is set in the constructor, and isAdmin is fine because it has a default value.
The fix is to assign email in the constructor, add a default value, or mark it as optional.
The remaining checks in brief
The strictFunctionTypes flag prevents you from assigning a function to a variable that expects more permissive parameter types. This catches subtle bugs where a callback handles fewer cases than the caller expects.
The strictBindCallApply flag checks that arguments passed to .call(), .bind(), and .apply() match the original function's parameter types. Without it, these methods accept anything and return any, hiding real mistakes.
The noImplicitThis flag flags uses of this where TypeScript cannot determine the type. This commonly catches mistakes in callback functions where this refers to the wrong context.
The alwaysStrict flag ensures every emitted file starts with "use strict", enabling ECMAScript strict mode behavior at runtime. This is a subtle but important correctness guarantee.
The useUnknownInCatchVariables flag changes catch clause variables from any to unknown, forcing you to narrow the error type before using it. This prevents accidentally accessing properties on an error object that might not be an Error instance at all:
try {
riskyOperation();
} catch (err) {
if (err instanceof Error) {
console.log(err.message);
}
}With this flag on, err is unknown instead of any. You must check its type before accessing properties, which catches cases where non-Error values are thrown.
Relaxing individual checks
You can enable strict but turn off a specific check if it causes too much friction:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": false
}
}This keeps all other strict checks active while allowing implicit any. This pattern is common during JavaScript-to-TypeScript migrations, where adding annotations to every parameter at once would be overwhelming.
The recommended approach: start with all checks on, then disable one at a time only when you have a concrete reason. It is easier to relax a check than to add it later when your codebase already has hundreds of implicit violations.
Strict mode and the compiler
When you enable strict, the TypeScript compiler applies every check during type-checking. These checks do not change the emitted JavaScript. The same .ts source produces identical .js output whether strict is on or off.
What changes is how many errors you see before the JavaScript is produced. More errors at compile time means fewer surprises at runtime. For an overview of how the compiler works with your configuration, see the TypeScript compiler explained.
Rune AI
Key Insights
- strict: true enables nine type-checking flags at once.
- strictNullChecks makes null and undefined distinct types that cannot be used where a real value is expected.
- noImplicitAny catches parameters and variables the compiler cannot type.
- strictFunctionTypes checks function parameter compatibility more correctly.
- strictPropertyInitialization ensures class properties are set in the constructor.
- You can turn off individual strict checks while keeping the rest active.
Frequently Asked Questions
Can I turn off individual checks after enabling strict?
Does strict mode affect runtime performance?
Should I always use strict mode?
Conclusion
Strict mode is the single most impactful setting in your tsconfig. It enables nine type-checking flags that catch entire categories of bugs at compile time: implicit any, null and undefined misuse, incorrect function type assignments, uninitialized class properties, and more. Enable it from the start of every new project.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.