When working with JavaScript and TypeScript, understanding the differences between null and undefined is crucial for writing predictable and bug-free code. While both represent an absence of a value, they have distinct meanings and behaviors. Let's have a look at their differences, common use cases, and best practices.

What is undefined?

In JavaScript and TypeScript, undefined means that a variable has been declared but has not been assigned a value.

let foo;
console.log(foo); // undefined

When does undefined occur?

  • A variable is declared but not assigned a value.
  • A function does not return anything explicitly.
  • Accessing a non-existent object property.
  • A missing function parameter (unless a default value is provided).

Example:

function greet(name?: string) {
  console.log("Hello, " + name);
}

greet(); // Hello, undefined

What is null?

null represents the intentional absence of any object value. It must be explicitly assigned.

let bar: null = null;
console.log(bar); // null

When is null used?

  • To indicate an intentional absence of a value.
  • When resetting an object or variable.
  • When working with DOM elements that may not exist.

Example:

let user = { name: "Alice" };
user = null; // User object is cleared

Key Differences Between null and undefined

Feature undefined null
Type undefined object (legacy quirk)
Default Value Assigned by JavaScript when a variable is declared but not initialized Must be explicitly assigned
Meaning Absence of assignment Intentional absence of value
Behavior in JSON Excluded by default Included explicitly
Usage Missing function parameters, uninitialized variables Explicitly clearing values

TypeScript Considerations

In TypeScript, the handling of null and undefined can be stricter with strictNullChecks. When enabled, variables cannot be null or undefined unless explicitly allowed.

let x: string;
x = undefined; // Error with strictNullChecks enabled

To allow both null and undefined:

let y: string | null | undefined;
y = null; // OK
y = undefined; // OK
y = "Hello"; // OK

Caveats When Checking Variables

When checking variables, different approaches yield different results:

let x;
if (!x) {
  console.log("x is falsy");
}

This condition is true for null, undefined, 0, NaN, "" (empty string), and false, which may lead to unintended behavior.

let x = null;
if (x === null) {
  console.log("x is explicitly null");
}

This condition is true only when x is null, making it a precise check for intentional absence.

let x;
if (x === undefined) {
  console.log("x is explicitly undefined");
}

This condition is true only when x is undefined, ensuring clarity when distinguishing between null and undefined values.

Best Practices

  • Use null when you want to explicitly indicate that a value is absent.
  • Avoid unnecessary undefined assignments.
  • Enable strictNullChecks in TypeScript to catch potential issues.
  • When handling optional properties, use optional chaining (?.) to avoid runtime errors.

Example:

let person: { name?: string } = {};
console.log(person.name?.toUpperCase()); // undefined, but no error

Conclusion

Understanding null and undefined helps in writing clearer and more predictable code. While undefined typically signifies an uninitialized value, null is used for explicitly clearing or indicating the absence of a value. With TypeScript’s strict null checks, handling these properly can lead to safer and more maintainable applications.