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.

6 min read

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:

FlagWhat it catchesExample bug prevented
strictNullChecksUsing null or undefined where a real value is expectedAccessing .name on a find() result that might be undefined
noImplicitAnyVariables and parameters the compiler cannot typeA function parameter with no type annotation
strictFunctionTypesAssigning a function where parameter types do not match safelyPassing a string-only callback where string-or-number is expected
strictBindCallApplyWrong argument types to .call(), .bind(), .apply()Calling fn.call(undefined, false) when fn expects a string
strictPropertyInitializationClass properties declared but not set in the constructorA name field with no initializer and no constructor assignment
noImplicitThisthis expressions where the type cannot be determinedUsing this inside a regular function callback
alwaysStrictFiles not parsed in ECMAScript strict modeAdds "use strict" to every emitted file
strictBuiltinIteratorReturnBuilt-in iterator .next() results typed as any after doneReading a property off a finished iterator's value without a type error
useUnknownInCatchVariablesCatch clause variables defaulting to anycatch (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:

typescripttypescript
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:

texttext
'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:

typescripttypescript
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:

typescripttypescript
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:

jsonjson
{
  "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

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.
RunePowered by Rune AI

Frequently Asked Questions

Can I turn off individual checks after enabling strict?

Yes. Set strict: true and then explicitly set individual flags to false. For example, strict: true with noImplicitAny: false keeps all other strict checks active while allowing implicit any. This is common during JavaScript migrations.

Does strict mode affect runtime performance?

No. All strict checks happen at compile time only. The emitted JavaScript is the same whether strict is on or off. Strict mode only affects what TypeScript considers an error before your code runs.

Should I always use strict mode?

Yes, for new TypeScript projects. The only reason to avoid it is when incrementally migrating a large JavaScript codebase, where you might enable checks one at a time to manage the number of new errors.

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.