TypeScript Comparison Operators: Numbers, Strings, Dates

How ===, !==, <, >, <= and >= behave in TypeScript for numbers, strings, dates and objects, and which comparisons the compiler rejects.

angular-typescript

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.

OperatorMeaningExampleResult
===Strict equal: same type and same valuea === 10true
!==Strict not equala !== btrue
==Loose equal: converts types firsta == bfalse
!=Loose not equala != btrue
>Greater thana > bfalse
<Less thana < btrue
>=Greater than or equala >= 10true
<=Less than or equala <= 5false

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.

Happy Learning !!

Source Code on Github

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.