Skip to content

[Feature request]type level equal operator #48100

@DetachHead

Description

@DetachHead
Contributor

original issue: #27024

Search Terms

  • Type System
  • Equal

Suggestion

T1 == T2

Use Cases

TypeScript type system is highly functional. Type level testing is required. However, we can not easily check type equivalence. I want a type-level equivalence operator there.

It is difficult for users to implement any when they enter. I implemented it, but I felt it was difficult to judge the equivalence of types including any.

Examples

type A = number == string;// false
type B = 1 == 1;// true
type C = any == 1;// false
type D = 1 | 2 == 1;// false
type E = Head<[1,2,3]> == 1;// true(see:#24897)
type F = any == never;// false
type G = [any] == [number];// false
type H = {x:1}&{y:2} == {x:1,y:2}// true
function assertType<_T extends true>(){}

assertType<Head<[1,2,3]> == 1>();
assertType<Head<[1,2,3]> == 2>();// Type Error

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
    This wouldn't change the runtime behavior of existing JavaScript code
    This could be implemented without emitting different JS based on the types of the expressions
    This isn't a runtime feature (e.g. new expression-level syntax)

workarounds

to summarize the discussion in #27024, the accepted solution was:

export type Equals<X, Y> =
    (<T>() => T extends X ? 1 : 2) extends
    (<T>() => T extends Y ? 1 : 2) ? true : false;

however there are many edge cases where it fails:

there were some other workarounds posted that attempted to address these problems, but they also had cases where they didn't work properly.

what "equal" means

i think it's important for it to treat structurally equal types as equal. for example { a: string, b: number; } should be considered equal to { a: string } & { b: number; } as they behave exactly the same

Activity

RyanCavanaugh

RyanCavanaugh commented on Mar 3, 2022

@RyanCavanaugh
Member

What I see in that thread is that people have differing definitions of "equal" that are possibly in conflict, e.g. #27024 (comment). If you want to reopen this please fill out something more substantial in the template that goes into the shortcomings, current trade-offs, and desired semantics.

DetachHead

DetachHead commented on Mar 4, 2022

@DetachHead
ContributorAuthor

@RyanCavanaugh i don't think there was any disagreement on the definition of "equal" but rather people finding edge cases that the workarounds didn't account for. as far as i can tell everyone in that discussion seems to agree on what types should be considered equal.

regardless, i've updated the issue with more information. can it be re-opened (or can the original issue be re-opened)?

RyanCavanaugh

RyanCavanaugh commented on Mar 4, 2022

@RyanCavanaugh
Member
{ p: string | number; } should be considered equal to { p: string } | { p: number; }

👀

These types are very much different; T extends { p: string } ? true : false is true | false for one and false for the other

added
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this feature
SuggestionAn idea for TypeScript
on Mar 4, 2022
DetachHead

DetachHead commented on Mar 5, 2022

@DetachHead
ContributorAuthor

oops, updated with a better example

HeavenSky

HeavenSky commented on Jul 19, 2022

@HeavenSky
// for example, below also is true
type M = Equal<{ x: 1 } & { y: 2 }, { x: 3, y: 2 }>
HeavenSky

HeavenSky commented on Jul 19, 2022

@HeavenSky
// for example, below also is true,  incorrect
type M = Equal<{ x: 1 } & { y: 2 }, { x: 3, y: 2 }>

So, this solution is not quite right.

export type Equal<X, Y> =
    (<T>() => T extends X ? 1 : 2) extends
    (<T>() => T extends Y ? 1 : 2) ? true : false;
ecyrbe

ecyrbe commented on Jul 23, 2022

@ecyrbe
// for example, below also is true,  incorrect
type M = Equal<{ x: 1 } & { y: 2 }, { x: 3, y: 2 }>

So, this solution is not quite right.

export type Equal<X, Y> =
    (<T>() => T extends X ? 1 : 2) extends
    (<T>() => T extends Y ? 1 : 2) ? true : false;

if you want to handle this case. you can use this :

type Simplify<T> = T extends unknown ? { [K in keyof T]: T[K] } : T;

type Equal<X, Y> =
    (<T>() => T extends Simplify<X> ? 1 : 2) extends
    (<T>() => T extends Simplify<Y> ? 1 : 2) ? true : false;

type M = Equal<{ x: 3 } & { y: 2 }, { x: 3, y: 2 }>

Playground Link

22 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @rauschma@andrewbaxter@ecyrbe@louwers@tommy-mitchell

        Issue actions

          [Feature request]type level equal operator · Issue #48100 · microsoft/TypeScript