Strict Mode Deep Dive
MedTypeScript strict mode is a set of compiler flags that enable stricter type checking. The `strict` flag in tsconfig.json is a shorthand that enables multiple individual strict checks. Understanding each strict flag, what errors it catches, and why it matters is important for interviews and for configuring TypeScript projects correctly.
Interactive Visualization
Key Points
- `strict: true` enables all strict flags as a bundle
- `strictNullChecks` makes null and undefined distinct types that must be handled
- `strictFunctionTypes` enforces contravariant parameter type checking
- `strictBindCallApply` enforces correct typing for bind, call, and apply
- `noImplicitAny` errors when TypeScript cannot infer a type and falls back to any
- `strictPropertyInitialization` requires class properties to be initialized in the constructor
- `useUnknownInCatchVariables` types catch variables as unknown instead of any
Code Examples
strictNullChecks
// WITHOUT strictNullChecks: null is assignable to any type // const name: string = null // no error (dangerous!) // WITH strictNullChecks: must explicitly allow null let name: string | null = null // Force handling of nullable values function getUser(id: string): User | undefined { // ...implementation return undefined } const user = getUser('1') // user.name // Error: Object is possibly 'undefined' // Safe access patterns if (user) { console.log(user.name) // narrowed to User } const displayName = user?.name ?? 'Anonymous' // optional chaining + nullish coalescing interface User { name: string; email: string }
strictNullChecks is the most impactful strict flag. Without it, null and undefined can be assigned to any type, defeating the purpose of type checking for one of the most common error sources.
noImplicitAny & strictPropertyInitialization
// noImplicitAny — catches untyped parameters // function greet(name) { } // Error: Parameter 'name' implicitly has an 'any' type // Must explicitly type parameters function greet(name: string): string { return `Hello, ${name}` } // Array methods need type context // [1, 2].reduce((acc, val) => acc + val) // might error without context const sum = [1, 2].reduce((acc: number, val: number) => acc + val, 0) // strictPropertyInitialization — class properties must be initialized class UserService { // name: string // Error: not initialized and no initializer // Options to fix: name: string = '' // 1. Default value age: number // 2. Initialized in constructor email!: string // 3. Definite assignment assertion (use sparingly) nickname: string | undefined // 4. Allow undefined constructor(age: number) { this.age = age } }
noImplicitAny catches untyped parameters and variables. strictPropertyInitialization ensures class properties are assigned before use, preventing undefined access errors.
strictFunctionTypes & useUnknownInCatchVariables
// strictFunctionTypes — enforces contravariant parameter checking class Animal { name = '' } class Dog extends Animal { breed = '' } type Handler<T> = (item: T) => void // With strictFunctionTypes: const animalHandler: Handler<Animal> = (a) => console.log(a.name) // const dogHandler: Handler<Dog> = animalHandler // Error: Dog handler cannot accept a broader Animal handler // (Animal could be Cat, which has no 'breed') // useUnknownInCatchVariables — catch blocks use unknown try { JSON.parse('invalid') } catch (err) { // With useUnknownInCatchVariables: err is unknown if (err instanceof SyntaxError) { console.log(err.message) // safely narrowed } // Without it: err is any (unsafe!) // console.log(err.message) // no error but no safety } // The strict flag bundle // { // "compilerOptions": { // "strict": true, // // Equivalent to enabling ALL of these: // // "strictNullChecks": true, // // "strictFunctionTypes": true, // // "strictBindCallApply": true, // // "strictPropertyInitialization": true, // // "noImplicitAny": true, // // "noImplicitThis": true, // // "useUnknownInCatchVariables": true, // // "alwaysStrict": true // } // }
strictFunctionTypes prevents unsafe function type assignments. useUnknownInCatchVariables makes catch blocks type-safe by using unknown instead of any, requiring narrowing before access.
Common Mistakes
- Not enabling strict mode in new projects — always start with `strict: true`
- Using the `!` non-null assertion to silence strictNullChecks instead of handling null properly
- Adding `// @ts-ignore` to bypass strict errors instead of fixing the type
- Not understanding that `strict: true` is a bundle of flags, not a single check
- Disabling individual strict flags without understanding the safety they provide
Interview Tips
- Know all the individual flags that `strict: true` enables
- Explain why strictNullChecks is the most impactful flag with a concrete example
- Mention that useUnknownInCatchVariables makes error handling type-safe
- Be ready to discuss tradeoffs of strict mode in legacy migration scenarios