Types and inference

Handle null and undefined deliberately

Include absent values in a type and check them before using the value under strict null checking.

A huge share of JavaScript crashes come down to one message: Cannot read properties of undefined. TypeScript’s answer is to make absence part of the type. If a value may be missing, the type says so, and the compiler makes you deal with it.

Here is a function that accepts a string that might not be there:

function uppercase(value: string | undefined) {
  return value === undefined ? '' : value.toUpperCase()
}

The === undefined check narrows value. In the second branch, TypeScript knows it can only be a string, so toUpperCase() is allowed. Skip the check and call value.toUpperCase() directly, and the compiler stops you:

error TS18048: 'value' is possibly 'undefined'.

That error is the whole feature. The crash that would have happened at runtime, on some unlucky input, is now a compile-time message pointing at the exact expression.

This only works with strict null checking turned on. Without it, null and undefined silently fit into every type and you get none of this protection. We enable strict when we set up the compiler config, and I would never turn it off.

Two kinds of absence

JavaScript has both null and undefined, and TypeScript keeps them separate. Match your types to what the data really contains.

Missing object properties and functions with no return produce undefined. An API might send null on purpose: JSON has null but no undefined, so a parsed response never contains undefined. If a field can be null in the response, type it as string | null, not string | undefined.

Do not silence the check

There is a shortcut, and I want you to know it so you can avoid it:

value!.toUpperCase()

The ! is the non-null assertion. It tells the compiler “trust me, this is not missing”. It adds no runtime check. If the value is missing, JavaScript still crashes, with exactly the error strict null checking exists to prevent. Every ! in a codebase is a place where the type system was overruled by hope.

Defaults with ??

Often the right behavior is a default value:

const label = value ?? 'Unknown'

The ?? operator, nullish coalescing, uses the default only when value is null or undefined. Compare it with value || 'Unknown', which also replaces '', 0 and false. Those are often legitimate values. Reach for ?? when only real absence should trigger the fallback.

After either an explicit check or ??, TypeScript narrows the type, and the rest of the code works with a plain string.

Try this on your own: write a function that accepts number | null. Make sure 0 passes through as a valid input, and return a default only for null. Then swap ?? for || and watch what happens to the zero.

Lesson completed