A TypeScript comparison operator checks how two values relate to each other and returns true or false. TypeScript uses the eight JavaScript comparison operators, namely ===, !==, ==, !=, <, >, <= and >=.
We use comparison operators in conditions and in sort functions, so a wrong comparison produces wrong results without any error. For example, a shop that sorts prices stored as text shows “100” before “20”. TypeScript helps here, because the compiler rejects comparisons between types that can never match, such as a number and a string.
The following example shows the operators on numbers, strings, dates and objects, with the results from TypeScript 7.0.2 on Node.js 22 as comments.
const a: number = 10;
const b: number = 20;
// 1. Equality
const isTen = a === 10; // isTen = true
const differs = a !== b; // differs = true
// 2. Greater and less
const less = a < b; // less = true
const atLeast = a >= 10; // atLeast = true
// 3. Strings compare character by character
const s1 = "apple" < "banana"; // s1 = true
const s2 = "Zebra" < "apple"; // s2 = true, uppercase first
const s3 = "10" < "9"; // s3 = true, not numeric
// 4. Alphabetical order for people
const order = "apple".localeCompare("Banana"); // order = -1
// 5. Dates compare by time
const later = new Date(2026, 0, 2) > new Date(2026, 0, 1); // later = true
// 6. Objects compare by reference
const lokesh = { age: 37 };
const copy = { age: 37 };
const sameObject = lokesh === copy; // sameObject = false
Notice that “Zebra” sorts before “apple” and that two objects with the same fields are not equal, which often confuses Java developers.
Next, we look at how each operator treats numbers, strings, dates, objects, and the null and undefined values. We also see the compiler errors TypeScript reports for suspicious comparisons.
1. Comparison Operators in TypeScript
The eight operators fall into two groups.
- The equality operators test whether two values are the same.
- The relational operators test their order.
Every operator returns a boolean, and the results in the table use a = 10 and b = 20.
| Operator | Meaning | Example | Result |
|---|---|---|---|
| === | Strict equal: same type and same value | a === 10 | true |
| !== | Strict not equal | a !== b | true |
| == | Loose equal: converts types first | a == b | false |
| != | Loose not equal | a != b | true |
| > | Greater than | a > b | false |
| < | Less than | a < b | true |
| >= | Greater than or equal | a >= 10 | true |
| <= | Less than or equal | a <= 5 | false |
The loose operators == and != convert the operands to a common type before comparing, so in JavaScript 10 == “10” is true. We use === and !== in TypeScript code, with one common exception, value == null, which matches both null and undefined. The conversion rules of == and === have more details.
2. Comparing Numbers
Number comparisons work as expected until NaN appears, which is short for “not a number” and is the result of an invalid calculation such as 0 / 0 or Number(“abc”). For example, a quiz app that parses a score from an empty form field gets NaN, and the score fails both the pass check and the fail check. Every comparison with NaN returns false, including NaN === NaN, so a NaN value fails both score > 1 and score <= 1.
const score: number = 0 / 0;
// 1. NaN is never greater, less or equal
const above = score > 1; // above = false
const below = score <= 1; // below = false
const isNan = Number.isNaN(score); // isNan = true
// 2. Range check: two comparisons joined with &&
const age = 37;
const inRange = 18 <= age && age < 60; // inRange = true
// 3. Default sort() compares as strings
const asText = [10, 9, 1].sort(); // asText = [1, 10, 9]
const asNumbers = [10, 9, 1].sort((x, y) => x - y); // asNumbers = [1, 9, 10]
To test for NaN, we call Number.isNaN(). Since TypeScript 4.9, writing score === NaN is a compile error (TS2845, the condition always returns false). A range check needs two comparisons joined with &&, and section 6 shows why 18 <= age < 60 does not work.
The method sort() of an array uses comparisons too, and without a compare function it converts the numbers to strings, so 10 lands before 9. The compare function (x, y) => x – y returns a negative number when x should come first, which gives the numeric order.
3. Comparing Strings
The relational operators compare strings one character at a time by UTF-16 code unit (that is, the number that represents each character). All uppercase letters have smaller codes than lowercase letters, and digits are compared as characters, not as numbers. For that reason, “Zebra” comes before “apple”, and “10” comes before “9”.
For text that people read, such as a customer list sorted by name, we use localeCompare(). The method returns a number whose sign gives the order.
- A negative number when the first string comes first.
- 0 when both strings are equal.
- A positive number otherwise.
The specification defines only the sign, and Node.js 22 returns -1, 0 or 1.
// 1. Character code order
const s1 = "Zebra" < "apple"; // s1 = true
const sorted = ["banana", "apple", "Cherry"].sort(); // sorted = ["Cherry", "apple", "banana"]
// 2. localeCompare(): negative, 0 or positive
const r1 = "apple".localeCompare("Banana"); // r1 = -1
const r2 = "a".localeCompare("A", undefined, { sensitivity: "base" }); // r2 = 0
const byName = ["banana", "apple", "Cherry"].sort((x, y) => x.localeCompare(y));
// byName = ["apple", "banana", "Cherry"]
// 3. Numbers inside strings
const collator = new Intl.Collator("en", { numeric: true });
const files = ["item10", "item9", "item1"].sort(collator.compare);
// files = ["item1", "item9", "item10"]
The option sensitivity: “base” makes the comparison ignore case and accents, so “a” and “A” are equal. An Intl.Collator with numeric: true reads digit groups as numbers, so “item9” comes before “item10”. A collator object is also faster than repeated localeCompare() calls when we sort a large array.
4. Comparing Dates
A Date object stores a timestamp, which is the number of milliseconds since 1 January 1970 UTC. The relational operators <, >, <= and >= convert both dates to that number first, so they give the correct order. The equality operators do not convert, so === on two Date objects checks whether both variables point to the same object. A booking app that checks whether two bookings start at the same time must keep that rule in mind.
const start = new Date(2026, 0, 1);
const same = new Date(2026, 0, 1);
// 1. Relational operators compare the time value
const before = start < new Date(2026, 5, 1); // before = true
const notAfter = start <= same; // notAfter = true
// 2. === compares references
const equalRef = start === same; // equalRef = false
const equalTime = start.getTime() === same.getTime(); // equalTime = true
To check whether two dates are the same moment, we compare getTime() values. We cover more date operations in TypeScript Date.
5. Comparing Objects and Arrays
JavaScript compares objects and arrays by reference (that is, the address of the object in memory), so === returns true only when both sides are the same object. Two objects with the same fields are still two different objects.
const lokesh = { name: "Lokesh", age: 37 };
const alias = lokesh;
const copy = { name: "Lokesh", age: 37 };
// 1. Same reference
const r1 = lokesh === alias; // r1 = true
// 2. Same content, different object
const r2 = lokesh === copy; // r2 = false
// 3. Compare the fields instead
const r3 = lokesh.name === copy.name && lokesh.age === copy.age; // r3 = true
For a flat object, comparing the fields that matter is the clearest option. For example, an address form compares the street and the city of the old and new address to decide whether to save. Calling JSON.stringify() on both sides also works for plain data, but it fails when the same keys appear in a different order. When both sides are literals, TypeScript reports the mistake, and [1, 2] === [1, 2] gives error TS2839, because JavaScript compares objects by reference, not by value.
6. Comparisons the Compiler Rejects
In plain JavaScript, 37 == “37” is true and “37” > 30 converts the string before comparing. TypeScript treats both comparisons as bugs, so it reports an equality check between types with no common value, and a relational check between a string and a number. Values from form fields and URL parameters are strings, so the compiler check catches a common mistake.
const input = "37";
const age = 37;
const same = age === input;
const older = input > 30;
const between = 30 < age < 40;
type Status = "new" | "done";
const statuses: Status[] = ["new", "done"];
const first = statuses[0];
const closed = first === "closed";
The TypeScript 7.0 compiler reports an error for every comparison in the snippet.
src/err.ts(4,14): error TS2367: This comparison appears to be unintentional because the types 'number' and 'string' have no overlap.
src/err.ts(5,15): error TS2365: Operator '>' cannot be applied to types 'string' and 'number'.
src/err.ts(6,17): error TS2365: Operator '<' cannot be applied to types 'boolean' and 'number'.
src/err.ts(11,16): error TS2367: This comparison appears to be unintentional because the types 'Status' and '"closed"' have no overlap.
The third error explains the range check from section 2. The expression 30 < age < 40 runs from left to right, so 30 < age returns a boolean, and the compiler does not allow comparing a boolean with 40. The last error catches a typo or an outdated value in a union of literal types. The value “closed” is not a Status, so the check could never be true. For the first two, we convert the input before comparing.
const same = Number(input) === age; // same = true
const older = Number(input) > 30; // older = true
7. null and undefined in Comparisons
At runtime, JavaScript converts null to 0 for < and >=, so null >= 0 is true, whereas undefined becomes NaN and every relational check with it is false. In strict mode, TypeScript blocks both cases, so a value whose type includes undefined, such as the result of Map.get(), cannot be used with a relational operator until we handle the missing case.
const scores = new Map<string, number>([["Lokesh", 80]]);
const raj = scores.get("Raj");
const passed = raj > 50;
src/err.ts(3,16): error TS18048: 'raj' is possibly 'undefined'.
We either check the value first, which narrows its type to number, or replace the missing value with a default using the nullish coalescing operator ??.
// 1. Check before comparing
const passed = raj !== undefined && raj > 50; // passed = false
// 2. Or give a default
const passedOrZero = (raj ?? 0) > 50; // passedOrZero = false
const lokeshPassed = (scores.get("Lokesh") ?? 0) > 50; // lokeshPassed = true
The ?? operator returns the right side only when the left side is null or undefined. It works together with the other TypeScript logical operators, and the two empty values differ as described in undefined vs null.
8. Running the Comparison Examples
All snippets of the article, except the ones marked as not compiling, are in the comparison-operators folder of the TypeScript-Examples repository. It needs Node.js 22 or newer and installs TypeScript 7.0.2.
npm install
npm start
The output lists each section under its own header and prints the values from the snippet comments.
9. Conclusion
TypeScript comparison operators return a boolean and follow JavaScript rules at runtime. Strings compare by character code and dates by timestamp, whereas objects compare by reference.
We prefer === and !==, and we use localeCompare() or Intl.Collator for human-readable text order. For date equality we compare getTime() values, and for NaN we call Number.isNaN(). The compiler adds safety on top of the runtime rules by rejecting comparisons between unrelated types and chained range checks, and by rejecting values that might be undefined.
10. References
The runtime rules come from MDN, and the compiler checks are described in the TypeScript Handbook and release notes.
- MDN: Expressions and operators, relational and equality operators
- MDN: Equality comparisons and sameness
- MDN: String.prototype.localeCompare()
- MDN: Intl.Collator
- TypeScript Handbook: Narrowing
- TypeScript 4.9 release notes: checks for equality on NaN
Happy Learning !!