TypeScript - null vs. undefined

Hello, aspiring programmers! Today, we're going to dive into an exciting topic that often confuses beginners: the difference between null and undefined in TypeScript. Don't worry if you're feeling a bit overwhelmed – I remember when I first encountered these concepts, I was scratching my head too! But by the end of this lesson, you'll be a pro at distinguishing between these two special values. Let's get started!

TypeScript - null vs. undefined

What is null?

In TypeScript (and JavaScript), null is a special value that represents the intentional absence of any object value. It's like saying, "Hey, there's supposed to be something here, but right now, there's nothing."

Let's look at some examples to understand this better:

let myPet: string | null = null;
console.log(myPet); // Output: null

// Later in the code...
myPet = "Fluffy";
console.log(myPet); // Output: Fluffy

In this example, we declare a variable myPet that can be either a string or null. Initially, we set it to null, indicating that we don't have a pet yet. Later, when we get a pet, we assign the name "Fluffy" to myPet.

Here's another example:

function findUser(id: number): { name: string } | null {
    // Imagine we're searching a database
    if (id === 1) {
        return { name: "Alice" };
    } else {
        return null;
    }
}

let user = findUser(1);
console.log(user); // Output: { name: "Alice" }

user = findUser(2);
console.log(user); // Output: null

In this case, our findUser function returns either a user object or null if no user is found. This is a common pattern in programming – using null to indicate that a search or operation didn't yield a result.

What is undefined?

Now, let's talk about undefined. This special value represents a variable that has been declared but hasn't been assigned a value yet. It's like an empty box – it exists, but there's nothing in it yet.

Here are some examples to illustrate undefined:

let myName: string;
console.log(myName); // Output: undefined

// Later in the code...
myName = "John";
console.log(myName); // Output: John

function greet(name?: string) {
    console.log(name);
}

greet(); // Output: undefined
greet("Alice"); // Output: Alice

In the first part, we declare myName but don't assign a value. TypeScript automatically gives it the value undefined. Later, we assign a value, and it's no longer undefined.

In the greet function, we use an optional parameter. If we call the function without providing an argument, the name parameter will be undefined.

Here's another scenario where you might encounter undefined:

let person = {
    name: "Bob",
    age: 30
};

console.log(person.name); // Output: Bob
console.log(person.job); // Output: undefined

In this case, person.job is undefined because we never defined a job property for our person object.

Null vs. Undefined: Key Differences

Now that we've explored null and undefined separately, let's compare them side by side to understand their differences better.

Aspect null undefined
Meaning Intentional absence of any object value Variable declared but not assigned a value
Type Object Undefined
In JSON Valid Invalid
Default function parameter Not used as default Used as default for optional parameters
Equality null == undefined (true), null === undefined (false) undefined == null (true), undefined === null (false)

Let's look at some code examples to illustrate these differences:

// Type checking
console.log(typeof null);       // Output: "object"
console.log(typeof undefined);  // Output: "undefined"

// JSON serialization
console.log(JSON.stringify({ a: null }));     // Output: {"a":null}
console.log(JSON.stringify({ a: undefined })); // Output: {}

// Default function parameters
function sayHello(name: string = "World") {
    console.log(`Hello, ${name}!`);
}

sayHello();        // Output: Hello, World!
sayHello(undefined); // Output: Hello, World!
sayHello(null);    // Output: Hello, null!

// Equality
console.log(null == undefined);  // Output: true
console.log(null === undefined); // Output: false

In practice, the choice between null and undefined often comes down to personal or team preference. However, understanding the differences can help you write more precise and bug-free code.

Here's a final example to tie everything together:

function processUser(user: { name: string, age?: number } | null | undefined) {
    if (user === null) {
        console.log("User explicitly set to null");
    } else if (user === undefined) {
        console.log("User not provided");
    } else {
        console.log(`Processing user: ${user.name}, Age: ${user.age ?? "Unknown"}`);
    }
}

processUser(null);                    // Output: User explicitly set to null
processUser(undefined);               // Output: User not provided
processUser({ name: "Alice" });       // Output: Processing user: Alice, Age: Unknown
processUser({ name: "Bob", age: 30 }); // Output: Processing user: Bob, Age: 30

This function demonstrates how we might handle null, undefined, and valid user objects differently in a real-world scenario.

And there you have it! You've just learned the ins and outs of null and undefined in TypeScript. Remember, practice makes perfect, so don't be afraid to experiment with these concepts in your own code. Happy coding, and may your variables always be intentionally null or undefined!

Credits: Image by storyset