After this lesson you can
- Say what strictNullChecks changes about every type
- Handle a value that may be null without reaching for a cast
- Recognise the casts that quietly disable the checker
Without strict, TypeScript is a linter with opinions. With it, the
compiler starts catching the errors that actually reach production. Turn it
on in tsconfig.json and leave it on.
{ "compilerOptions": { "strict": true } }
strictNullChecks
The single most valuable flag in the set. Without it, null and undefined
are members of every type, so string silently means "a string, or nothing
at all". With it, they are separate:
function greet(name: string | null): string {
return `Hi ${name.toUpperCase()}`; // error: name may be null
}
You then handle it, and the handling is visible in the code rather than living in someone's memory:
if (name === null) return "Hi there";
return `Hi ${name.toUpperCase()}`;
?. and ?? are for the cases where a default is genuinely right:
const display = user.profile?.name ?? user.email;
Try it
interface Profile { name?: string }interface User { email: string; profile?: Profile } function display(user: User): string { // Both hops may be missing, so both are optional-chained, and the // fallback is a real value rather than "undefined" printed to a page. return user.profile?.name ?? user.email;} export function run(): string[] { return [ display({ email: "nino@example.com" }), display({ email: "ana@example.com", profile: {} }), display({ email: "luka@example.com", profile: { name: "Luka" } }), ];}The escape hatches, and what they cost
as Type— an assertion. You are telling the compiler you know better. If you are wrong, nothing catches it and the failure surfaces somewhere else.!— the non-null assertion.user!.namesays "trust me". The same deal, in one character, which is what makes it easy to scatter.any— the checker off entirely for that value, and for anything derived from it.
All three are occasionally the right answer, usually at a boundary where you genuinely know something the compiler cannot. Each one is also a place where a future bug will not be caught, so each one deserves a comment saying why it is safe.
The habit worth building: when the compiler complains, ask what it has noticed before you ask how to silence it. It is usually right.
Try it yourself
2 visible tests · 2 hidden testsImplement safeDivide(a: number, b: number): number | null. Return
a / b, except when b is 0 — dividing by zero has no answer, so
return null rather than Infinity, -Infinity or NaN, and make the
caller deal with that explicitly.
safeDivide(10, 2)safeDivide(7, 0)
Sign up to check the hidden tests and save your progress. Sign up