HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
5 Topics
Chapter•70 Minutes

Types, Values, Equality, and Coercion

Go beyond primitive type lists and understand JavaScript's value model, coercion rules, equality algorithms, and the surprising cases that appear in real applications.

Primitive Values

JavaScript has seven primitive data types: string, number, bigint, boolean, undefined, symbol, and null. Primitives are immutable values. Objects, functions, arrays, dates, maps, sets, and other compound structures are objects.

number uses IEEE 754 double-precision floating-point representation, which means integers and fractional values share the same type. bigint exists for integers outside the safe integer range of number.

Inspecting Primitive Types
console.log(typeof 'hello');
console.log(typeof 42);
console.log(typeof 42n);
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof Symbol('id'));
console.log(typeof null);
💡 Most typeof results match the conceptual type. The famous exception is typeof null, which returns "object" for historical compatibility.
Key Takeaways
  • Primitive values are immutable.
  • BigInt is intended for arbitrary-size integers.
  • typeof null returning object is a historical language quirk.
Knowledge Check

How many primitive data types does modern JavaScript define?

Numbers, NaN, Infinity, and Precision

JavaScript's number type is a 64-bit IEEE 754 floating-point value. This means many decimal fractions cannot be represented exactly in binary. Therefore 0.1 + 0.2 is not exactly 0.3.

The largest integer that can be represented safely as an integer by number is Number.MAX_SAFE_INTEGER. For exact arithmetic involving larger integers, use BigInt. NaN means the result is not a valid numeric value and has the unusual property that it is not equal to itself.

Floating-Point Precision
console.log(0.1 + 0.2);
console.log(Number.isNaN(NaN));
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER));
console.log(9007199254740993n);
💡 Floating-point representation creates rounding behavior for some decimals. BigInt provides exact integer arithmetic for values beyond the safe integer range, although BigInt cannot be freely mixed with Number.
Key Takeaways
  • Do not assume decimal arithmetic is exact.
  • Use Number.isNaN rather than global isNaN when validating actual NaN values.
  • Use BigInt for integer calculations that exceed Number's safe range.
Knowledge Check

Why does `0.1 + 0.2 === 0.3` evaluate to false?

Truthy and Falsy Values

JavaScript converts values to boolean in contexts such as if, logical operators, and conditional expressions. Falsy values include false, 0, -0, 0n, "", null, undefined, and NaN. Everything else is truthy, including empty arrays and empty objects.

This distinction is important because developers often incorrectly assume that an empty array or object is falsy.

Truthy Does Not Mean Non-Empty
if ([]) {
  console.log('empty array is truthy');
}

if ({}) {
  console.log('empty object is truthy');
}
💡 Objects are truthy regardless of whether they contain properties or elements.
Key Takeaways
  • Empty arrays and objects are truthy.
  • Falsy is a specific set of values, not a general concept of emptiness.
  • Use explicit checks when zero, empty strings, or null-like values have different meanings.
Knowledge Check

Which value is falsy?

Strict Equality vs Loose Equality

=== uses strict equality and does not perform the ordinary type coercion associated with ==. For most application code, strict equality makes intent easier to understand. Loose equality has a defined coercion algorithm and is not random, but its behavior is sufficiently complex that it should be used deliberately.

Remember that NaN !== NaN, while Object.is(NaN, NaN) is true. Object.is also distinguishes 0 and -0.

Equality Algorithms
console.log(1 === '1');
console.log(1 == '1');
console.log(NaN === NaN);
console.log(Object.is(NaN, NaN));
console.log(Object.is(0, -0));
💡 Strict equality does not coerce 1 and '1'. Loose equality does. Object.is uses SameValue semantics, making NaN equal to itself and distinguishing positive and negative zero.
Key Takeaways
  • Prefer === for ordinary equality checks.
  • == performs a defined but sometimes surprising coercion process.
  • Object.is differs from === for NaN and signed zero.
Knowledge Check

Which expression evaluates to true?

Nullish Coalescing and Optional Chaining

The nullish coalescing operator ?? chooses the right-hand value only when the left-hand side is null or undefined. This differs from ||, which treats every falsy value as a reason to use the fallback.

Optional chaining ?. allows property, method, and element access to stop safely when the preceding value is nullish.

Why ?? Can Be Better Than ||
const pageSize = 0;

console.log(pageSize || 20);
console.log(pageSize ?? 20);

const user = {};
console.log(user.profile?.address?.city ?? 'Unknown');
💡 The || expression treats zero as falsy and returns 20. The ?? expression preserves zero because zero is not nullish.
Key Takeaways
  • Use ?? when only null and undefined should trigger a fallback.
  • Use || when any falsy value should trigger a fallback.
  • Optional chaining prevents errors while traversing nullish values.
Knowledge Check

What does `0 ?? 10` return?