Skip to main content

working-with-null-and-undefined

Understanding null and undefined:

null is when you know something is empty and you've marked it as such. undefined is when something hasn't been given a value yet. They're like two different ways of saying "no data": one is intentional, and the other is just not filled in yet.

Strict Null Checks:

By enabling strict null checks, you're making sure that you don't accidentally use null or undefined where a real value is needed. It's a precaution to prevent mistakes.

{
"compilerOptions": {
"strictNullChecks": true
}
}

Union Types with null/undefined:

Union types let you say "this can be a number, or it can be null, or it can be undefined". It's explicitly stating all the possibilities for what a value can be.

let age: number | null | undefined;
age = null; // Okay
age = undefined; // Okay
age = 25; // Also okay

Optional Parameters and Properties:

Optional properties in TypeScript are like saying, "You can give me this information if you have it, but it's okay if you don't."

function greet(name: string, greeting?: string) {
// greeting is optional, it can be a string or undefined.
console.log(`${greeting || 'Hello'}, ${name}`);
}

Definite Assignment Assertions:

Definite assignment assertions are like promising TypeScript that you will assign a value later on. It's a way to tell TypeScript, "Trust me, I've got this covered."

let myVar!: number;
initialize();
console.log(2 * myVar); // TypeScript trusts that myVar is assigned.

function initialize() {
myVar = 10; // Assignment happens here.
}

Type Guards for null/undefined:

Type guards are like quality checks. They make sure you're not working with null or undefined by accident, avoiding errors down the line.

function doSomething(value: string | null) {
if (value == null) {
// value is either null or undefined here.
console.log("No value provided.");
return;
}
// TypeScript knows value is a string here.
console.log(value.toUpperCase());
}

Nullish Coalescing:

Nullish coalescing is a way to handle null or undefined by providing a default value. It's like having a backup when something you expected to be there isn't.

const response = {
settings: null
};

const defaultSettings = { theme: "dark" };
const settings = response.settings ?? defaultSettings;
// settings will be the defaultSettings if response.settings is null or undefined.

These practices help ensure that you are handling the absence of values in a safe and predictable way, reducing bugs and making your code easier to understand.

Optional Chaining:

Optional chaining lets you read the value of a property without worrying if its parent is null or undefined. It's like tapping a domino that may or may not fall, but either way, you're safe.

const user = {
profile: {
name: 'Alice',
details: {
age: 25,
// ... other details
}
}
};

const userAge = user.profile?.details?.age;
// If profile or details are null/undefined, userAge will be undefined.

Type Assertions with null/undefined:

When you're sure that a value won't be null or undefined, you can use a type assertion to tell TypeScript to trust you.

function getButton(): HTMLButtonElement | null {
// ... some logic to get the button
}

const button = getButton();
if (button) {
// TypeScript knows button is not null here.
button.click();
}

Default Parameters:

Default parameters in functions provide a fallback value for arguments that are not provided or are undefined.

function createGreeting(message: string, userName = "stranger") {
// userName will be "stranger" if not provided
return `${message}, ${userName}!`;
}

Using null as a Clear Signal:

Choosing null to represent an intentional absence of value makes your code's intention clear.

function clearUser(user: User | null) {
if (user === null) {
// Explicit check for 'null' indicates it's a deliberate absence of a user.
console.log("No user to clear.");
}
// ... logic to clear the user
}

Discriminated Unions with null/undefined:

Discriminated unions that include null or undefined force you to handle those cases, ensuring you don't forget to account for them.

type Response = { data: string } | null | undefined;

function handleResponse(response: Response) {
if (response === null) {
// Handle null
} else if (response === undefined) {
// Handle undefined
} else {
// Handle { data: string }
}
}

Avoiding null and undefined Pitfalls:

Learn the common issues that come up with null and undefined to avoid them in your own code.

Migrating with Strict Null Checks:

If you're moving an old project to use strict null checks, do it piece by piece to make sure you don't get overwhelmed.

Understanding Compiler Options:

Dive into the TypeScript compiler settings so you know how to make it work best for you, including how it handles null and undefined.

{
"compilerOptions": {
"strictNullChecks": true,
"strictPropertyInitialization": true
}
}

These tips give you the knowledge to write TypeScript code that's ready for the challenges of null and undefined, reducing bugs and making maintenance easier.