TypeScript's strict mode is not a single flag. It is a collection of compiler options that, when enabled together, significantly raise the bar for code correctness. Most discussions about strict mode focus on developer experience and code quality. But several strict mode checks have direct security implications that deserve attention.
Enabling "strict": true in tsconfig.json activates all strict checks at once. Understanding what each check prevents helps you appreciate why this matters for security.
strictNullChecks
This is the most impactful strict mode option for security. Without it, null and undefined are assignable to every type. This means any value in your program could be null at runtime, and TypeScript will not warn you.
The security implications are concrete. Consider an authentication check that returns a user object or null. Without strictNullChecks, you can pass that potentially-null value to authorization logic without any compiler error. At runtime, the null value might be interpreted as a falsy value that bypasses a security check, or it might cause an uncaught exception that reveals error details to an attacker.
With strictNullChecks enabled, the compiler forces you to handle the null case explicitly. You must check for null before accessing properties, which means authentication failures cannot silently propagate through your code as null values.
strictPropertyInitialization
This option requires that class properties declared without initializers are assigned in the constructor. Without it, you can declare a property of type SecurityContext and forget to initialize it. The property will be undefined at runtime despite the type system saying it is a SecurityContext.
For security-relevant objects like authentication tokens, permission sets, or encryption contexts, uninitialized properties can lead to conditions where security objects are used before they are properly set up.
noImplicitAny
Without this option, TypeScript silently assigns the any type to variables and parameters whose types cannot be inferred. The any type disables all type checking for that value. It can be passed anywhere, called as a function, accessed with any property name -- all without compiler errors.
In security contexts, any types defeat the purpose of TypeScript entirely. An API response typed as any can be used without validation. A function parameter typed as any accepts any input without type checking. If your security logic relies on type checking to enforce constraints, any types create holes in that enforcement.
strictBindCallApply
This option ensures that bind, call, and apply are type-checked. Without it, these methods accept any arguments without verification. An attacker who can influence the arguments to a call or apply invocation could bypass type-level constraints.
strictFunctionTypes
This enables contravariant checking for function parameter types. Without it, TypeScript uses bivariant checking, which is unsound. In security terms, this means a function expecting a specific input type could be replaced with one accepting a broader type, potentially allowing inputs that the original function was designed to reject.
Beyond Strict Mode
Several additional compiler options complement strict mode for security:
noUncheckedIndexedAccess treats array and object index access as potentially undefined. Without it, accessing array[i] is typed as the element type even if i is out of bounds. With it, the access is typed as T | undefined, forcing you to handle the out-of-bounds case.
exactOptionalPropertyTypes distinguishes between a property being absent and being explicitly set to undefined. This matters for configuration objects where the presence or absence of a security setting has different semantics than explicitly setting it to undefined.
noPropertyAccessFromIndexSignature forces bracket notation for index signature access, making it clear when you are accessing a dynamically-named property versus a statically-known one. This helps prevent property confusion attacks.
Real-World Impact
A study of TypeScript projects that adopted strict mode found that approximately 15% of the bugs caught by strictNullChecks were in error handling or security-relevant code paths. These were bugs that existed in production, were not caught by tests, and were found only when the compiler enforced null checking.
The pattern is consistent: security logic tends to handle edge cases and error conditions. These are exactly the code paths where null values, uninitialized properties, and untyped parameters cause the most damage.
Migration Strategy
Enabling strict mode on an existing codebase can produce thousands of compiler errors. The pragmatic approach is incremental:
Start with strictNullChecks since it has the highest security impact. Fix the errors it reveals, starting with authentication, authorization, and input validation code.
Add noImplicitAny next. This forces explicit typing throughout the codebase, which improves both security and maintainability.
Enable the remaining strict options incrementally. Use // @ts-expect-error sparingly and only with a comment explaining why the error is acceptable.
For new projects, always start with "strict": true. There is no good reason to start a new TypeScript project without strict mode.
The Supply Chain Dimension
Type safety in TypeScript is only as strong as your type definitions. If you use a library with incorrect or incomplete type definitions (either bundled or from DefinitelyTyped), the compiler may accept code that is incorrect at runtime.
Malicious type definitions are a real if uncommon threat. A type definition package on DefinitelyTyped that declares a function as returning string when it actually returns string | null creates a hole that strict mode cannot catch. Always verify that your type definitions come from trusted sources and are actively maintained.
How Safeguard.sh Helps
Safeguard.sh monitors your TypeScript project dependencies -- both runtime packages and type definition packages -- for known vulnerabilities. It generates SBOMs that capture the full dependency tree including @types packages, and alerts you when vulnerabilities or suspicious changes are detected in any dependency. Combined with strict mode enforcement in your TypeScript configuration, Safeguard.sh provides both compile-time type safety and runtime dependency security for your TypeScript applications.