void, null & undefined

Reviewed & published by Brayan K

void is the return type of a function that yields no useful value, undefined means a value is absent or not yet set, and null means a value is intentionally empty — TypeScript tracks all three so missing data can't surprise you.

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn

💡 Real-World Analogy

Think of a parcel locker slot. undefined is a slot that was never filled — nobody put anything in. null is a slot that staff deliberately emptied and labelled "reserved, intentionally empty". void is the receipt you get for an action like "lock the door": the action happened, but the receipt itself carries no useful contents to read.

1. void: Functions That Return Nothing

A function annotated : void is called for its side effect — logging, saving, updating the DOM — not for a value. At runtime it still returns undefined, but void is the type that tells callers "don't use my return value." There's a subtle but important rule: a void return type in a callback means "I'll ignore whatever you return," so callbacks that do return something are still accepted.

// 'void' = "this function's return value is not meant to be used":
function log(msg: string): void {
  console.log(msg);
  // return 5; // ❌ Type 'number' is not assignable to type 'void'
}

// The "void in callbacks" rule: a void return type means "I ignore whatever
// you return", so a callback that DOES return something is still accepted:
const ids: number[] = [];
[1, 2, 3].forEach((n): void => { ids.push(n); }); // ✅ push returns a number
//   forEach expects (n: number) => void, but returning a number is fine —
//   void in this position means "I won't look at your return value".
// 'void' is the return type of a function that returns NOTHING useful.
// In TS:  function log(msg: string): void { console.log(msg); }
// At runtime such a function still returns 'undefined' — 'void' is just the
// TYPE that says "ignore my return value".

function logMessage(msg) {
  console.log("LOG: " + msg);
  // no return statement -> returns undefined at runtime
}

const result = logMessage("saved"); // LOG: saved
console.log(result);                 // undefined

// 'void' vs 'undefined': a void function MAY return undefined, but callers
// should not rely on the value at all. It signals "called for its side-effect".
function noop() {}                    // : void
console.log(noop());                 // undefined

2. null vs undefined

These two represent "nothing" but carry different intent. undefined is what you get from an uninitialised variable, a missing object property, or a missing argument — "not there." null is a value you assign on purpose to mean "deliberately empty." They're distinct (null === undefined is false), and an optional property age? adds undefined to a type, whereas manager: User | null opts into null.

// Optional '?' adds 'undefined' to the type; '| null' adds null on purpose:
interface User {
  name: string;
  age?: number;        // means: number | undefined  (may be ABSENT)
  manager: User | null; // means: a User, or deliberately NO manager
}

const u: User = { name: "Ada", manager: null };
// u.age is 'number | undefined' — TS makes you handle the undefined case.

// 'age?: number' and 'age: number | undefined' are ALMOST the same, but
// only the '?' version lets you OMIT the property entirely when building u.
// 'undefined' means "this has no value YET / a field is absent".
// 'null'      means "intentionally empty — we deliberately set it to nothing".
// They are TWO different values in JavaScript, and TypeScript tracks both.

let notAssigned;                 // undefined automatically
console.log(notAssigned);        // undefined
console.log(typeof notAssigned); // undefined

let cleared = null;              // we chose 'empty on purpose'
console.log(cleared);            // null
console.log(typeof cleared);     // object  (a famous JS quirk!)

// They are NOT equal with === :
console.log(null === undefined);  // false
// ...but loosely equal with ==  (both are "nullish"):
console.log(null == undefined);   // true

// Optional property (?) gives 'undefined' when missing; an explicit
// '| null' field is something you set to null on purpose:
const user = { name: "Ada" };    // 'age?' omitted
console.log(user.age);           // undefined

3. strictNullChecks, ?. and ??

With strictNullChecks on (part of strict), a plain string can't secretly be null — you must opt in and then prove the value is present before using it. Two operators make that painless at runtime: optional chaining ?. short-circuits to undefined instead of crashing, and nullish coalescing ?? supplies a fallback for only null or undefined (unlike ||, which also replaces 0 and "").

// With strictNullChecks ON, this is an error you WANT:
function len(s: string | null): number {
  // return s.length;   // ❌ 's' is possibly 'null'
  return s?.length ?? 0; // ✅ optional chain + nullish fallback
}

// ?. stops at the first null/undefined and yields undefined:
//   user?.profile?.bio   ->  undefined if user OR profile is missing
// ?? supplies a default for null/undefined ONLY (keeps 0, "", false):
//   count ?? 10          ->  10 only when count is null/undefined
// strictNullChecks: with it ON (recommended), null/undefined are NOT allowed
// in a normal type — you must opt in with '| null' or '| undefined' and then
// CHECK before use. The runtime tools for this are ?. and ??.

// Optional chaining ?. short-circuits to undefined instead of crashing:
const user = { profile: null };
console.log(user.profile?.bio);          // undefined  (no crash)
console.log(user.profile?.bio ?? "n/a"); // n/a        (?? fills the gap)

// ?? only falls back on null OR undefined (NOT on 0 or "" — unlike ||):
console.log(0 ?? 99);    // 0    (0 is a real value)
console.log(null ?? 99); // 99
console.log("" ?? "x");  // ""   (prints an EMPTY line — "" is a real value)
console.log("" || "x");  // x    (|| treats "" as falsy — different!)

// A safe accessor that handles a possibly-missing value:
function bioOf(u) {
  return u?.profile?.bio ?? "No bio provided";
}
console.log(bioOf(user));        // No bio provided
console.log(bioOf(undefined));   // No bio provided

🎯 Your Turn

Supply a default greeting only when the name is missing. Fill in the blank marked ___ with the right operator, then run it.

// 🎯 YOUR TURN — replace the ___ then press "Try it Yourself".
// 'name' might be null or undefined; supply a default ONLY in those cases.
// Hint: the nullish coalescing operator is two question marks: ??

function greet(name) {
  return "Hi, " + (name ___ "guest"); // 👉 use ?? so 0/"" wouldn't be replaced
}

console.log(greet("Ada"));      // Hi, Ada
console.log(greet(null));       // Hi, guest
console.log(greet(undefined));  // Hi, guest

// ✅ Expected output:
//    Hi, Ada
//    Hi, guest
//    Hi, guest

Common Errors (and the fix)

Pro Tips

📚 Keep learning

Mini-Challenge: Safe Display Name

No blanks this time — just a brief and a starting outline. Build the name builder yourself, run it, and check your output against the example in the comments.

// 🎯 MINI-CHALLENGE: build a display name that handles missing data.
// A user object may have first/last names, or be entirely absent (null).
//
// Write fullName(user) that:
//   1. Returns "Unknown" if user itself is null/undefined.
//   2. Uses "" as a fallback for a missing first or last name (??).
//   3. Trims the joined "<first> <last>" — if the result is empty, return "Unknown".
//
// Test with: { first:"Ada", last:"Lovelace" }, { first:"Grace" }, null, and {}.
//
// ✅ Expected output:
//    Ada Lovelace
//    Grace
//    Unknown
//    Unknown

// your code here

🎉 Lesson Complete

Practice quiz

The 'void' type is used mainly as:

  • A value you store in a variable
  • An array element type
  • A function's return type for side-effects
  • A replacement for null

Answer: A function's return type for side-effects. void marks a function that returns nothing useful — callers should ignore its return value.

At runtime, a function typed to return void actually returns:

  • undefined
  • null
  • 0
  • An empty string

Answer: undefined. Such a function still returns undefined at runtime; void is just the type signalling 'ignore it'.

What does 'undefined' typically mean?

  • Intentionally emptied
  • A syntax error
  • An empty array
  • A value is absent or not yet set

Answer: A value is absent or not yet set. undefined is what you get from an uninitialised variable, a missing property, or a missing argument.

What does 'null' typically convey?

  • A value that was never declared
  • A value deliberately set to empty
  • A function with no body
  • A frozen object

Answer: A value deliberately set to empty. null means intentionally empty — you assign it on purpose to mean 'no value'.

What is the result of null === undefined?

  • false
  • true
  • An error
  • undefined

Answer: false. They are distinct values, so strict equality null === undefined is false (though null == undefined is true).

typeof null evaluates to:

  • 'null'
  • 'undefined'
  • 'object'
  • 'void'

Answer: 'object'. typeof null is the string 'object' — a long-standing JavaScript quirk; test null with === null.

An optional property written as age?: number means its type is:

  • number only
  • number | undefined
  • number | null
  • never

Answer: number | undefined. The optional ? adds undefined to the type and lets you omit the property entirely.

Turning on strictNullChecks means a plain 'string' type:

  • Secretly includes null and undefined
  • Becomes any
  • Cannot be used
  • Genuinely excludes null and undefined

Answer: Genuinely excludes null and undefined. strictNullChecks removes the hidden null/undefined membership; you must opt in with | null or | undefined.

The optional chaining operator ?. on a missing value yields:

  • null
  • undefined
  • An exception
  • 0

Answer: undefined. ?. short-circuits and yields undefined instead of crashing when something is null or undefined.

Unlike ||, the nullish coalescing operator ?? falls back ONLY on:

  • 0 and empty string too
  • false only
  • null or undefined
  • Any truthy value

Answer: null or undefined. ?? supplies a default only for null or undefined, so 0, '', and false are kept as real values.

Continue this course

Frequently asked questions

What's the difference between void and undefined?

'undefined' is a real value a variable can hold. 'void' is a return type meaning 'this function returns nothing you should use' — at runtime such a function still returns the value undefined, but 'void' is a signal to callers to ignore it. You use 'undefined' as a value type (e.g. number | undefined) and 'void' almost exclusively as a function return type.

Should I use null or undefined for 'no value'?

Both work; consistency matters more than the choice. A common convention: undefined means 'not provided / absent' (what an optional property or missing argument gives you), while null means 'explicitly set to nothing' (e.g. a database column you cleared). Many codebases lean on undefined throughout and reserve null for APIs that specifically return it. Pick one style per project and stick to it.

What does strictNullChecks actually do?

With strictNullChecks off, every type silently includes null and undefined, so the compiler never warns you about possibly-missing values. Turning it on (it's part of 'strict') removes that hidden membership: a 'string' is now genuinely a string, and if a value can be missing you must say so with '| null' or '| undefined' and check before using it. It's the single most valuable flag for catching the billion-dollar null bug.

When do I use ?? versus ||?

Use ?? (nullish coalescing) when you want a default ONLY for null or undefined. Use || when you want a default for any falsy value (null, undefined, 0, '', false, NaN). The classic bug is writing 'count || 10' which wrongly replaces a legitimate 0 with 10; '?? ' keeps the 0. Reach for ?? for numbers, booleans, and strings where empty is a valid value.