Thu. Jun 5th, 2025

A solid understanding of null, undefined, void, and never empowers you to write cleaner, more predictable code in both JavaScript and TypeScript. In JavaScript, undefined is the default value for uninitialized variables and missing returns, while null represents an intentional absence of a value. TypeScript builds on these primitives by introducing void for functions that return nothing meaningful and never for functions that never complete normally. By mastering these four constructs, you’ll reduce bugs, improve type safety, and enhance code readability and maintainability.


1. JavaScript Primitives: ‘undefined’ vs. ‘null’

1.1 What Is ‘undefined’?

  • Definition: A variable declared without initialization automatically holds undefined.
  • Occurrence: Accessing a nonexistent property or calling a function without an explicit return also yields undefined.
  • Behavior: It’s a falsy value, so a simple equality check (x === undefined) reliably detects missing data.
js

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

function greet() {}
console.log(greet()); // undefined

1.2 What Is ‘null’?

  • Definition: An explicit assignment by the developer to indicate “no value.”
  • Usage: Clear or reset variables when you want to differentiate between uninitialized (undefined) and intentionally empty (null) states.
  • Behavior: Also falsy, but semantically signals intentional absence.
js

let result = null;
console.log(result); // null

1.3 Key Differences

Aspectundefinednull
How It ArisesAutomatically for uninitialized varsMust be explicitly assigned
Semantic MeaningMissing or omitted valueIntentional absence of a value
Typical Checkif (x === undefined) {…}if (x === null) {…}
Falsy?YesYes

2. TypeScript Function Types: ‘void’ vs. ‘never’

2.1 What Is ‘void’?

  • Definition: Indicates that a function does not return any meaningful value.
  • Return Behavior: May implicitly return undefined, but callers should ignore the return.
  • Use Case: Logging, state updates, event handlers—any function used solely for side effects.
ts

function logMessage(message: string): void {
console.log(message);
}

2.2 What Is ‘never’?

  • Definition: Represents code paths that never successfully complete.
  • Behavior: Used for functions that always throw errors or run infinite loops.
  • Use Case: Error handlers, exhaustive type checks in switch statements.
ts

function throwError(msg: string): never {
throw new Error(msg);
}

function endless(): never {
while (true) {}
}

2.3 Comparing ‘void’ and ‘never’

Aspectvoidnever
Completes NormallyYesNo
Returns ValueImplicit undefinedNo value (unreachable)
Common UseSide-effect-only functionsError generation, unreachable code detection

3. Practical Patterns & Best Practices

3.1 Nullish Coalescing for Defaults

Use ?? to handle both null and undefined:

js

function getUserName(user) {
return user.name ?? "Guest";
}

3.2 Strict Equality Checks

Differentiate clearly between null, undefined, and other falsy values:

js

if (value === null) {
// only matches null
} else if (value === undefined) {
// only matches undefined
}

3.3 Exhaustive Checks with ‘never’

Ensure your switch statements cover all cases:

ts

type Status = "open" | "closed";

function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}

function handle(status: Status) {
switch (status) {
case "open":
// ...
break;
case "closed":
// ...
break;
default:
assertNever(status); // Catches unhandled cases at compile time
}
}