TypeScript

Strict mode

The compiler flags that turn TypeScript from decoration into a safety net.

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

A value that might not be theretypescript-5
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" } }),  ];}
What to look for

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!.name says "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 tests

Implement 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)
Loading editor…

Sign up to check the hidden tests and save your progress. Sign up