Quick Overview
Without `strictNullChecks`, `null` is assignable to every type and the compiler hides the entire class of null bugs. Turning it on is what makes `?.` and `??` meaningful rather than decorative.
Tony Hoare called the invention of null references "my billion-dollar mistake" due to endless null pointer crashes. TypeScript solves this problem using the strictNullChecks compiler flag.
strictNullChecks DoesWhen strictNullChecks: true is enabled in tsconfig.json, null and undefined are NOT assignable to other types unless explicitly included in a union:
let username: string = "Alex";
// username = null; // Error: Type 'null' is not assignable to type 'string'
let optionalEmail: string | null = null; // Explicit union required!
Adding a ? to a property or parameter marks it as optional, automatically making its type type | undefined:
interface Config {
apiKey: string;
timeout?: number; // Inferred as 'number | undefined'
}
function connect(config: Config) {
// Safe default fallback using Nullish Coalescing (??)
const timeoutMs = config.timeout ?? 5000;
console.log(`Connecting with timeout: ${timeoutMs}ms`);
}
type UserResponse = {
profile?: {
avatarUrl?: string;
};
};
function getAvatar(res: UserResponse): string {
// Safe optional chaining ?.
return res.profile?.avatarUrl ?? "/default-avatar.png";
}
strictNullChecks: Setting strictNullChecks: false turns off null checking globally, re-introducing silent runtime TypeError: Cannot read properties of undefined crashes. Always keep strictNullChecks: true enabled!Keep strict null checks enabled across all projects. Next, let's enter Phase 2 and master Functions, Overloads, and Narrowing!
TypeScript erases entirely at build time, so a `string` annotation produces no runtime check whatsoever. A value arriving from an API is whatever the server actually sent, regardless of what the annotation claims.
`let x = "GET"` infers `string` because the binding can change; `const x = "GET"` infers the literal `"GET"`. That widening rule is why object properties need `as const` to stay narrow.
`string[]` is any number of strings; `[string, number]` is exactly two, in that order. `readonly` removes the mutating methods at compile time, so `push` becomes a type error rather than a runtime surprise.