Skip to content

Proposal: Introduce a Relational Comparison Type Relationship #10120

Closed
@DanielRosenwasser

Description

@DanielRosenwasser

Problem

Before literal types and union types, it made sense that relational comparison operators and equality comparison operators had the same rules.

Overly-permissive

However, with union types, relational comparisons don't always make sense.

declare let x: string;
declare let y: string | number;

if (x < y) {
    // ...
}

Here, it's not clear that you'll get the "correct" behavior if x and y are not both strings, but TypeScript permits this. Issue #5156 covers this in more detail.

Overly-strict

While the current "comparable" relationship is the most lax relationship, it is also too strict. For instance, consider the following example from #10119:

function f(onethree: 1 | 3, two: 2) {
  return onethree < two;
}

Under the comparable relationship, TypeScript issues an error because the type 2 is not comparable with 1 nor is it comparable with 3.

Proposal

I propose a new relational comparison relationship, in which for any types S and T:

  • If T is a union type, then S is only relationally comparable if S is relationally comparable with each constituent of T.
  • Otherwise, if S is a union type, then S is only relationally comparable to T if each constituent of S is relationally comparable to T.
  • Otherwise, if S is string-like, then S is relationally comparable if T is string-like or Any.
  • Otherwise, if S is number-like, then S is relationally comparable if T is number-like or Any.
  • Otherwise, if S is Any, then S is relationally comparable if T is number-like, string-like, or Any.

To note:

  • Boolean and other unmentioned types are never relationally comparable to anything. The rationale is that you probably didn't want to compare a Boolean with a less-than operator.
  • Any is not relationally comparable to everything - for instance, you can't compare a Boolean with an Any.
  • There should not be a case where a mix of string-like and number-like types are ever relationally comparable.
  • Type parameters are currently not covered here (see below).

Open Questions

It's not clear how this behaves with type parameters. For instance, you might propose that we just check the type parameter's constraint here, but that's not enough. If you constrain T to number | string, then T will not be relationally comparable to itself because two distinct values of type T could each have different types at runtime. See #5156 (comment) for a concrete example.

Metadata

Metadata

Assignees

Labels

CommittedThe team has roadmapped this issueFixedA PR has been merged for this issueSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions