TypeScript null & undefined: A Comprehensive Tutorial with Code Examples

TypeScript null & undefined: A Comprehensive Tutorial with Code Examples

In TypeScript, null and undefined are important concepts that represent “nothing” or the absence of a value. Understanding how they work, how TypeScript treats them, and how to handle them properly in your code can help prevent common runtime errors.

This tutorial will guide you through the use of null and undefined, along with best practices and examples.

1. Introduction to null and undefined

In TypeScript (as in JavaScript), null and undefined are two special types used to represent the absence of a value:

undefined: Indicates that a variable has been declared but not assigned a value.
null: Indicates an intentional absence of any object or value.

Example 1: null and undefined in TypeScript

let value1: undefined = undefined;
let value2: null = null;

console.log(value1); // Output: undefined
console.log(value2); // Output: null

2. Differences Between null and undefined

While both null and undefined represent “nothing,” they are not the same:

undefined is the default value assigned to variables that are declared but not initialized.
null is a value that represents the intentional absence of any object value, and it must be explicitly assigned.

Example 2: Differences Between null and undefined

let uninitializedValue: undefined; // Implicitly undefined
let nullValue: null = null; // Explicitly null

console.log(typeof uninitializedValue); // Output: "undefined"
console.log(typeof nullValue); // Output: "object"

The typeof operator treats null as an object due to a quirk in JavaScript, whereas undefined is treated as its own type.

3. TypeScript’s strictNullChecks Option

When the strictNullChecks option is enabled in your TypeScript configuration (tsconfig.json), variables of types like string or number will not allow null or undefined unless explicitly specified. This is a crucial feature for type safety.

Example 3: With strictNullChecks Enabled

let name: string = "John";
// name = null; // Error: Type 'null' is not assignable to type 'string'
// name = undefined; // Error: Type 'undefined' is not assignable to type 'string'

If strictNullChecks is disabled, null and undefined can be assigned to any type.

Enabling strictNullChecks:
In tsconfig.json:

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

4. Handling null and undefined with Union Types

If you want to explicitly allow null or undefined for certain variables, you can use union types to define multiple valid types.

Example 4: Union Types with null and undefined

let username: string | null = null;
let age: number | undefined;

username = "Alice";  // Valid
age = 25;            // Valid
age = undefined;     // Valid

// username = 123; // Error: Type 'number' is not assignable to type 'string | null'

In this example, username can be either a string or null, and age can be a number or undefined.

5. Optional Parameters and Default Values

In TypeScript, function parameters can be marked as optional by appending a ?. This means that the parameter can be undefined if no value is provided.

Example 5: Optional Parameters

function greet(name?: string): string {
    if (name) {
        return `Hello, ${name}!`;
    } else {
        return "Hello!";
    }
}

console.log(greet("John"));  // Output: Hello, John!
console.log(greet());        // Output: Hello!

You can also provide default values for parameters to avoid dealing with undefined inside the function body.

Example 6: Default Parameter Values

function greetWithDefault(name: string = "Guest"): string {
    return `Hello, ${name}!`;
}

console.log(greetWithDefault("John")); // Output: Hello, John!
console.log(greetWithDefault());       // Output: Hello, Guest!

6. Nullish Coalescing (??)

The nullish coalescing operator (??) is a feature in TypeScript that returns the right-hand operand if the left-hand operand is null or undefined. It's a great way to provide fallback values.

Example 7: Nullish Coalescing

let userInput: string | null = null;
let defaultName = "Anonymous";

let name = userInput ?? defaultName;  // "Anonymous" because userInput is null

console.log(name);  // Output: Anonymous

In this example, ?? ensures that name will be “Anonymous” if userInput is either null or undefined.

7. Safe Access with Optional Chaining (?.)

Optional chaining (?.) is a feature that allows you to safely access deeply nested object properties or methods that might be null or undefined.

Example 8: Optional Chaining

type User = {
    name: string;
    address?: {
        city: string;
    };
};

let user: User = {
    name: "Alice",
    address: undefined
};

console.log(user.address?.city);  // Output: undefined (safe access)

Without optional chaining, attempting to access user.address.city would throw an error if address is undefined.

8. Best Practices for Dealing with null and undefined

Enable strictNullChecks: Enabling strictNullChecks ensures that you explicitly handle null and undefined and avoid accidental errors.

Use Optional Chaining (?.): Safely access nested properties without worrying about null or undefined.
Default Values with Nullish Coalescing (??): Use ?? to provide fallback values for potentially null or undefined variables.
Avoid Using null When Possible: In many cases, using undefined (the default) is sufficient and less confusing than null.
Use Union Types for Explicit Nullability: If a variable can be null or undefined, use union types like string | null or number | undefined.

Conclusion

Understanding how null and undefined work in TypeScript, along with the tools TypeScript provides to handle them (strictNullChecks, optional chaining, and nullish coalescing), will help you write safer, more robust code.

Whether you’re dealing with optional values or working with deeply nested objects, TypeScript provides mechanisms to ensure that you handle these potential pitfalls effectively.

Related posts

TypeScript Casting: A Comprehensive Tutorial with Code Examples

TypeScript Functions: A Comprehensive Tutorial with Code Examples

TypeScript Union Types: A Comprehensive Tutorial with Code Examples