Most TypeScript projects I analyze have "strict": true in their tsconfig.json, but they also have // @ts-ignore scattered through the code, any in strategic places, and engineers who don't know what strict mode protects.
Strict mode is not an arbitrary set of restrictions to make the compiler annoying. It's a collection of flags, each catching a category of bug that happens at runtime. Understanding what each flag does changes "TypeScript made me do this" to "TypeScript prevented me from making this error."
What "strict": true enables
"strict": true is a shortcut to enable a set of flags. Let's cover what matters:
strictNullChecks
The most important one. Without it, null and undefined are assignable to any type. With it, you must handle the possibility of absence.
// Without strictNullChecks: compiles, can explode at runtime
function getUser(id: string): User {
return db.find(id) // may return undefined
}
const name = getUser('123').name // TypeError: Cannot read property 'name' of undefined
// With strictNullChecks: compile-time error
function getUser(id: string): User | undefined {
return db.find(id)
}
const user = getUser('123')
const name = user?.name // forced to handle the undefined
noImplicitAny
Forces explicit typing when TypeScript can't infer the type. Without it, function parameters without types become any with no warning, and you lose all the protection of the type system.
// ❌ Without noImplicitAny: param is implicitly `any`
function process(data) { // param: any
return data.value.toUpperCase() // no checking
}
// ✅ With noImplicitAny: you declare the intent
function process(data: { value: string }) {
return data.value.toUpperCase()
}
strictFunctionTypes
Ensures correct type checking in callbacks. Without it, functions are checked bivariantly, which can let type bugs pass through.
// strictFunctionTypes catches this bug
type Handler = (event: MouseEvent) => void
const myHandler: Handler = (event: Event) => {} // ❌ Correct error: MouseEvent is more specific
strictPropertyInitialization
Ensures class properties are initialized in the constructor. Prevents reading undefined properties that appear to be defined.
class UserService {
private db: Database // ❌ Error: not initialized in constructor
constructor() {
// forgot to initialize db
}
}
// ✅ Correct
class UserService {
private db: Database
constructor(db: Database) {
this.db = db
}
}
Flags not in strict but that you should enable
noUncheckedIndexedAccess
Array and object index accesses return T | undefined instead of T. Prevents one of the most common bugs: assuming an index always exists.
// tsconfig.json
{ "compilerOptions": { "noUncheckedIndexedAccess": true } }
const items = ['a', 'b', 'c']
const first = items[0] // type: string | undefined (not string)
const safe = first?.toUpperCase() // forced to check
exactOptionalPropertyTypes
Differentiates { prop?: string } (prop may be absent) from { prop: string | undefined } (prop present but undefined). These are different things, and without this flag, TypeScript treats them as equal.
How to migrate a legacy project to strict
Enabling strict all at once in a large project is not practical. The strategy that works:
- Enable
"strict": trueand use// @ts-ignoreon everything that breaks (yes, temporarily) - Create a lint rule that prohibits new
// @ts-ignore, legacy can exist, new code cannot - Gradually resolve the
@ts-ignorecomments file by file, prioritizing the most critical ones
This gives the immediate benefit of strict on new code, while legacy is fixed over time.
Why not to disable what you don't understand
Every time you add // @ts-ignore, as any, or disable a flag, you're saying: "I trust myself more than the compiler here." Sometimes that's legitimate, when working with poorly typed libraries, for example.
Most of the time, it's an escape. TypeScript is protecting you from something that will explode at runtime. Maybe not tomorrow. Maybe in 6 months. Maybe in production on a Friday at 11 PM.
When you don't understand why TypeScript is complaining, the right answer is to understand, not silence it. Using TypeScript and using TypeScript well are different things.
Enjoyed this content?
I build web products and AI solutions the right way — solid architecture, maintainable code, and real delivery.
Let's talk