Difference Between undefined and null in JavaScript and TS

undefined means a value was never set, null means it was set to “nothing” on purpose. Learn where each comes from, how they differ, and how strictNullChecks, ?? and ?. handle them.

angular-typescript

In JavaScript and TypeScript, undefined means that a value was never set, and null means that a value was set to “nothing” on purpose. The difference between undefined and null is about intent. A variable declared without a value, a missing object property, a missing function argument and the result of a function without a return are all undefined, and the language produces them on its own. A null appears only when our code or our data puts it there, for example a null field in a JSON response.

Both values represent “no value”, so most code treats them the same way. We meet both in everyday code, for example when a user profile from an API has a null phone number and no middle name at all.

The following example shows the whole difference and the operators that deal with it, with the result of each line as a comment. The examples run on Node.js 22 and compile with TypeScript 7.0.2 in strict mode.

// 1. undefined: never set; null: set to "no value" on purpose
let notSet: string | undefined;               // notSet = undefined
const empty: string | null = null;            // empty = null

// 2. typeof
const t1 = typeof notSet;                     // t1 = "undefined"
const t2 = typeof empty;                      // t2 = "object"

// 3. Equality
const loose = null == undefined;              // loose = true
const strict = null === undefined;            // strict = false

// 4. ?? replaces only null and undefined, || replaces any falsy value
const count = 0;
const a = count ?? 10;                        // a = 0
const b = count || 10;                        // b = 10

// 5. Optional chaining stops at null or undefined
const user: { address?: { city: string } } = {};
const city = user.address?.city;              // city = undefined

// 6. One check for both values
const isMissing = empty == null;              // isMissing = true

Notice that typeof null returns “object”, and that ?? keeps the 0 in count, whereas || replaces it.

The two values differ in typeof and strict equality as well as in number conversion, JSON and default parameters. After those differences, we look at how TypeScript’s strictNullChecks option makes both values visible in the types, and at the operators that handle them, such as ?? (nullish coalescing) and ?. (optional chaining).

1. Where undefined Comes From

The JavaScript engine uses undefined as the default whenever something has no value, so we rarely write undefined ourselves. Most cases come from ordinary lookups that find nothing, such as a missing key in a map or an index past the end of an array.

// 1. Variable declared without a value
let age: number | undefined;                  // age = undefined

// 2. Missing property
const ages: Record<string, number> = { Lokesh: 37 };
const raj = ages["Raj"];                      // raj = undefined

// 3. Missing argument
function greet(name?: string): string {
  return "Hello, " + name;
}
const g = greet();                            // g = "Hello, undefined"

// 4. Function without a return value
function log(): void {}
const result = log();                         // result = undefined

// 5. Index out of range, no match, missing key
const second = [37][1];                       // second = undefined
const found = [37, 35].find((n) => n > 40);   // found = undefined
const fromMap = new Map<string, number>().get("John");   // fromMap = undefined

The second example hides a trap, because the compiler types raj as number, not number | undefined, since a Record<string, number> claims that every string key has a number. To make the compiler catch the missing key, we enable the noUncheckedIndexedAccess compiler option, which changes the type to number | undefined, so raj + 1 fails with error TS18048: ‘raj’ is possibly ‘undefined’.

A variable that was never declared is a different case, because reading it does not return undefined. It throws ReferenceError: undeclaredVar is not defined at runtime, and TypeScript reports it at compile time as Cannot find name.

2. Where null Comes From

The language never produces null on its own when something is missing. For example, a customer saved without a phone number gets null in the phone column, and the API sends that null in its JSON. A null value appears in one of these cases.

  • A programmer assigns it.
  • A built-in API returns it to say “nothing found”.
  • Data from JSON or a database contains it.
// 1. Set by the programmer
let winner: string | null = null;             // no winner yet
winner = "Raj";

// 2. Returned by APIs that found nothing
const match = "apple".match(/x/);             // match = null

// 3. Read from JSON
const data = JSON.parse('{"email": null}');
const email = data.email;                     // email = null

// 4. typeof null is "object"
const kind = typeof null;                     // kind = "object"

The result of typeof null is “object”, even though null is a primitive value and not an object. The “object” result is a mistake from the first version of JavaScript, and it was never fixed because changing it would break existing websites. To check for null, we compare with === null, never with typeof.

Java developers will recognize null as the “no object” reference, but JavaScript also has a second “no value” value, undefined, which Java does not have.

3. Differences Between undefined and null

The two values behave the same in an if condition (both are falsy) and with the ?? and ?. operators. They behave differently in typeof checks, strict equality, number conversion, JSON output and default parameters.

Operationundefinednull
typeof“undefined”“object”
Compared with == to the other valuetruetrue
Compared with === to the other valuefalsefalse
Number(value), or used in arithmeticNaN (100 + undefined is NaN)0 (100 + null is 100)
Property value in JSON.stringify()The property is left outWritten as null
Argument for a parameter with a defaultThe default is usedThe default is not used

The default-parameter row causes real bugs, because a function with name = “Guest” uses the default only for undefined, so a null from a database or JSON field gets through and prints as the text “null”.

// 1. Equality
const e1 = null == undefined;                 // e1 = true
const e2 = null === undefined;                // e2 = false
const e3 = null == 0;                         // e3 = false

// 2. Conversion to number
const n1 = Number(null);                      // n1 = 0
const n2 = Number(undefined);                 // n2 = NaN

// 3. JSON drops undefined properties, keeps null
const json = JSON.stringify({ a: undefined, b: null });   // json = {"b":null}

// 4. Default parameters replace only undefined
function greet(name: string | null = "Guest"): string {
  return "Hello, " + name;
}
const g1 = greet(undefined);                  // g1 = "Hello, Guest"
const g2 = greet(null);                       // g2 = "Hello, null"

The loose equality operator == treats null and undefined as equal to each other and to nothing else, not even 0 or false. The == rule makes value == null a short and safe check for “null or undefined“. The check value == null is the one place where many style guides allow ==, and everywhere else we use === instead of ==.

4. null and undefined in TypeScript: strictNullChecks

With the strictNullChecks option, which the strict option turns on, null and undefined are separate types, so a variable of type string cannot hold either of them. To allow them, we add them to the type with a union, such as string | null, and the compiler requires a check before every use. For example, a signup form with an optional nickname types the field as string | null, so every place that shows the nickname must handle the null case.

// 1. Allow null or undefined explicitly
let nickname: string | null = null;
let middleName: string | undefined = undefined;

// 2. Optional property: may be missing
type User = {
  name: string;
  email?: string;                             // string | undefined, key may be absent
  phone: string | undefined;                  // key must be present
};
const raj: User = { name: "Raj", phone: undefined };

// 3. Narrow before use
function length(text: string | null): number {
  if (text === null) {
    return 0;
  }
  return text.length;                         // text: string
}
const l1 = length("apple");                   // l1 = 5
const l2 = length(null);                      // l2 = 0

The optional property email?: string and the property phone: string | undefined look alike, but they differ.

  • An object can leave out email completely.
  • The phone property must be written, even if its value is undefined.

The compiler catches each of these mistakes.

let nickname: string = null;

type User = { name: string; email?: string; phone: string | undefined };
const john: User = { name: "John" };

function length(text: string | null): number {
  return text.length;
}
src/err.ts(1,5): error TS2322: Type 'null' is not assignable to type 'string'.
src/err.ts(4,7): error TS2741: Property 'phone' is missing in type '{ name: string; }' but required in type 'User'.
src/err.ts(7,10): error TS18047: 'text' is possibly 'null'.

Without strictNullChecks, all three lines compile, and the third one throws TypeError: Cannot read properties of null (reading ‘length’) at runtime, so we keep strict on in tsconfig.json for every new project. The non-null assertion operator ! (as in text!.length) turns the check off for one expression. We use it only when we know more than the compiler, because a wrong assertion brings the runtime error back.

5. Default Values: ?? vs ||

The nullish coalescing operator ?? returns its right side only when the left side is null or undefined, whereas the older || operator returns its right side for every falsy value, which also includes 0, the empty string “”, false and NaN. So for a count of 0 or an empty text, || replaces a valid value. For example, a shop page that shows stock || 10 claims 10 items for a product that is sold out.

const count = 0;
const label = "";
const missing: number | undefined = undefined;

// 1. ?? keeps 0 and ""
const c1 = count ?? 10;                       // c1 = 0
const l1 = label ?? "none";                   // l1 = ""
const m1 = missing ?? 10;                     // m1 = 10

// 2. || replaces every falsy value
const c2 = count || 10;                       // c2 = 10
const l2 = label || "none";                   // l2 = "none"

// 3. ??= assigns only when null or undefined
let stock: number | null = null;
stock ??= 5;                                  // stock = 5
stock ??= 9;                                  // stock = 5, unchanged

We use ?? for default values and keep || for boolean logic. The ??= operator, added in ES2021, is the short form of stock = stock ?? 5. The || operator is one of the logical operators, and its result depends on which values are truthy and falsy.

6. Optional Chaining With ?.

The optional chaining operator ?. reads a property or an array element, or calls a method, only when the value before it is not null or undefined. Otherwise, the whole expression returns undefined instead of throwing a TypeError, so together with ?? we can read a nested value with a default in one line. For example, a profile page reads user.address?.city for users who never filled in an address.

type User = {
  name: string;
  address?: { city: string };
  tags?: string[];
  greet?: () => string;
};
const john: User = { name: "John" };
const raj: User = { name: "Raj", address: { city: "Pune" }, tags: ["admin"] };

// 1. Property
const c1 = john.address?.city;                // c1 = undefined
const c2 = raj.address?.city;                 // c2 = "Pune"

// 2. Array element and method call
const tag = john.tags?.[0];                   // tag = undefined
const hello = john.greet?.();                 // hello = undefined

// 3. With a default value
const city = john.address?.city ?? "Unknown"; // city = "Unknown"

Optional chaining always returns undefined, even when the value it stopped at was null. When our code needs to tell the two apart, we compare the value with === null.

7. Which One Should We Use?

Many TypeScript code bases settle on undefined for “no value” in their own code. Optional properties, optional parameters and missing Map entries already produce undefined, so when we use it everywhere, we have one value to check instead of two.

We still use null where the outside world requires it.

  • JSON fields that must be present with an empty value
  • Database columns
  • APIs such as String.match() that return null

At these boundaries, a type such as string | null documents the contract, and when a value may be either one, value == null or the ?? operator handles both in one check.

8. Running the null and undefined Examples

The undefined-vs-null project on GitHub contains one file per section and an index.ts that prints every value from the snippet comments. The project compiles with TypeScript 7.0.2, and we need Node.js 22 or later to run it.

npm install
npm start

9. Conclusion

The value undefined is what JavaScript uses when nothing was assigned, and null is the value code assigns to say “empty”. They are equal with == and different with ===, and they also differ in typeof, number conversion, JSON output and default parameters. In TypeScript, strictNullChecks forces us to declare where either value is allowed and to check it before use. For defaults and nested reads, ?? and ?. treat both values the same way and leave falsy values such as 0 and “” untouched.

10. References

MDN documents the runtime behavior of both values and the operators, and the TypeScript Handbook describes strictNullChecks and narrowing.

Happy Learning !!

Source Code on Github

Leave a Comment

  1. Very good article, but according to the documentation null is in fact a primitive data type in Javascript, not an object.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.