Language-specific · 41
JavaScript equality and truthiness
== vs ===, falsy values, ?? vs ||, numbers that are all doubles, undefined vs null, and what TypeScript catches.
This lesson is for JavaScript · TypeScript.
Other languages do not quietly convert types when comparing.
In Python "5" == 5 is just False (and "5" < 5 is a TypeError), Java and C# refuse to compile it so you convert explicitly, and Craykon's == is false for different types.
JavaScript quietly changes types when it compares values or decides true and false.
The example below reads the text "5" and "0" and shows how == and ===, || and ??, floating-point sums, and undefined and null give different answers.
const [a, b] = require("fs").readFileSync(0, "utf8").trim().split("\n"); console.log(a == 5, a === 5, a === "5");console.log(a + 1, Number(a) + 1);console.log(0 == "", null == undefined, null === undefined); const falsy = [0, "", NaN, null, undefined, false];console.log(falsy.filter(Boolean).length, Boolean("0"), Boolean([])); const count = Number(b);console.log(count || 10, count ?? 10); console.log(0.1 + 0.2, 0.1 + 0.2 === 0.3);console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);console.log(Number.MAX_SAFE_INTEGER + 2);console.log(String(2n ** 64n)); const nan = Number("abc");console.log(nan === nan, Number.isNaN(nan)); const user = { name: "Kim" };console.log(user.age, typeof null, user.age ?? "none");import { readFileSync } from "node:fs"; const [a, b]: string[] = readFileSync(0, "utf8").trim().split("\n"); // a == 5는 컴파일 오류 TS2367(string과 number는 같을 수 없어요). any로 검사를 끄면 JS와 똑같이 돌아요.const raw: any = a;const zero: any = 0;console.log(raw == 5, raw === 5, a === "5");console.log(a + 1, Number(a) + 1);console.log(zero == "", null == undefined, null === undefined); const falsy: unknown[] = [0, "", NaN, null, undefined, false];console.log(falsy.filter(Boolean).length, Boolean("0"), Boolean([])); const count: number = Number(b);console.log(count || 10, count ?? 10); console.log(0.1 + 0.2, 0.1 + 0.2 === 0.3);console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);console.log(Number.MAX_SAFE_INTEGER + 2);console.log(String(2n ** 64n)); const nan: number = Number("abc");console.log(nan === nan, Number.isNaN(nan)); // user.age는 타입에 없으면 컴파일 오류 TS2339라서 age?: number로 적어 둬요const user: { name: string; age?: number } = { name: "Kim" };console.log(user.age, typeof null, user.age ?? "none");5 0
true false true 51 6 true true false 0 true true 10 0 0.30000000000000004 false true 9007199254740992 18446744073709551616 false true undefined object none
Key points
==converts one side when the types differ.
That is why"5" == 5,0 == ""andnull == undefinedare alltrue.===also requires the same type.
The conversion rules are complicated, so rather than memorize them, always use===and!==.+joins text when either side is text."5" + 1is"51", but"5" - 1is4.
Input is always text, so convert it withNumber(...)first.- The falsy values are only
false,0,-0,0n,"",null,undefinedandNaN."0"and an empty array[]are truthy (in Python[]is falsy). - There are two kinds of 'no value'.
undefinedmeans nothing was ever put there (a missing property, an argument not passed, the result of a function with noreturn);nullis an "empty" someone set on purpose.
To check for both, writex == nullorx ?? fallback. NaNis not even equal to itself, soNaN === NaNisfalse; ask withNumber.isNaN(x).typeof nullbeing"object"is a mistake left over from early JavaScript.
How each language writes it
| JavaScript | Compare with ===/!==, give defaults with ??, and turn text into numbers with Number(...).None of these traps warns you at run time |
|---|---|
| TypeScript | Blocks some of them at compile time: a == 5 (string vs number) is TS2367, x === NaN is TS2845, and user.age not in the type is TS2339.Under strict you must check age?: number for undefined before using it.But types are erased before running, so at run time it is plain JavaScript, and any or as turns the checks off (the example's raw) |
Common mistakes
Giving a default with ||
const count = 0;
console.log(count || 10, count ?? 10);→ 10 0 — || gives the right side whenever the left is falsy, so the valid value 0 turns into 10
Use ??, which gives the right side only for null or undefined.
Comparing floating-point numbers with ===
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);→ 0.30000000000000004, false — every number is one 64-bit floating-point type (double), so rounding errors appear
Compare the difference, as in Math.abs(x - y) < Number.EPSILON.
Doing math on a very large integer
console.log(Number.MAX_SAFE_INTEGER + 2);→ 9007199254740992 — integers are exact only up to 2^53 − 1 (9007199254740991), so you do not get ...993
Use BigInt with an n suffix, as in 2n ** 64n.
Mixing it with ordinary numbers, as in 1n + 1, is a TypeError.
Compare with
=== and give defaults with ??.== and || produce different answers without any error, such as "5" == 5 being true or 0 turning into 10.Practice
- Sale price, or list price if there is none8P0281
- Three hops to the power box contact8P0286
- Checking lock records8P0287
- Fill blanks with the last value8P0047
- Loose or strict equality2P0084
- Four ways to be the same2P0124
- Identical parcels and parcels with the same contents8P0205
- Comparing products rule by rule8P0301