Optional Properties & strictNullChecks
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.
Deep Dive
1. The Billion-Dollar Mistake
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.
2. What strictNullChecks Does
When 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!
3. Optional Properties (?)
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`);
}
4. Safe Optional Navigation
type UserResponse = {
profile?: {
avatarUrl?: string;
};
};
function getAvatar(res: UserResponse): string {
// Safe optional chaining ?.
return res.profile?.avatarUrl ?? "/default-avatar.png";
}
5. Common Mistakes
- Disabling
strictNullChecks: SettingstrictNullChecks: falseturns off null checking globally, re-introducing silent runtimeTypeError: Cannot read properties of undefinedcrashes. Always keepstrictNullChecks: trueenabled!
6. Next Steps
Keep strict null checks enabled across all projects. Next, let's enter Phase 2 and master Functions, Overloads, and Narrowing!
Next Up in TypeScript & Type System Mechanics
TypeScript Primitives & Explicit Type Annotations
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.
Type Inference & Literal Types (const vs. let)
`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.
Arrays, Tuples & Readonly Modifiers
`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.
