Skip to content

[Feature request]type level equal operator #27024

Closed
@kgtkr

Description

@kgtkr

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)

Activity

AlCalzone

AlCalzone commented on Sep 11, 2018

@AlCalzone
Contributor

Here's a working implementation:

/**
 * Tests if two types are equal
 */
export type Equals<T, S> =
	[T] extends [S] ? (
		[S] extends [T] ? true : false
	) : false
;

The only problem is that any is "equal to" everything, except never.

kgtkr

kgtkr commented on Sep 11, 2018

@kgtkr
Author

@AlCalzone
I know that.(https://github.com/kgtkr/typepark/blob/master/src/test.ts)
There is a problem of not being able to judge any.

example:

type X=Equals<{x:any},{x:number}>;//true
DanielRosenwasser

DanielRosenwasser commented on Sep 11, 2018

@DanielRosenwasser
Member

any is not assignable to never, so you should be able to determine whether or not either side is exclusively any.

mattmccutchen

mattmccutchen commented on Sep 15, 2018

@mattmccutchen
Contributor

Here's a solution that makes creative use of the assignability rule for conditional types, which requires that the types after extends be "identical" as that is defined by the checker:

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

This passes all the tests from the initial description that I was able to run except H, which fails because the definition of "identical" doesn't allow an intersection type to be identical to an object type with the same properties. (I wasn't able to run test E because I don't have the definition of Head.)

kgtkr

kgtkr commented on Sep 22, 2018

@kgtkr
Author

Thank you
There was a way

aleclarson

aleclarson commented on Apr 11, 2019

@aleclarson

The best solution I have to date: spec.ts

Examples

jituanlin

jituanlin commented on Jul 9, 2019

@jituanlin

Here's a solution that makes creative use of the assignability rule for conditional types, which requires that the types after extends be "identical" as that is defined by the checker:

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

This passes all the tests from the initial description that I was able to run except H, which fails because the definition of "identical" doesn't allow an intersection type to be identical to an object type with the same properties. (I wasn't able to run test E because I don't have the definition of Head.)

It work, but how?
Could you provide more explanation?
I try to explain it though by Typescript's bivariant behavior or something else.
But I failed, help, pls.

fatcerberus

fatcerberus commented on Jul 12, 2019

@fatcerberus

@jituanlin AFAIK it relies on conditional types being deferred when T is not known. Assignability of deferred conditional types relies on an internal isTypeIdenticalTo check, which is only true for two conditional types if:

  • Both conditional types have the same constraint
  • The true and false branches of both conditions are the same type
weakish

weakish commented on Dec 22, 2019

@weakish

@AlCalzone It seems that function overloads do not work.

type F = (x: 0, y: null) => void
type G = (x: number, y: string) => void

type EqEq<T, S> = [T] extends [S] ? ([S] extends [T] ? true : false) : false

// Function type intersection is defined as overloads in TypeScript.
type OF1 = EqEq<F & G, G & F> // true
type OF3 = EqEq<{ (x: 0, y: null): void; (x: number, y: null): void }, { (x: number, y: null): void; (x: 0, y: null): void }> // true

@mattmccutchen

Function overloads works:

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

type OF4 = EqEqEq<{ (x: 0, y: null): void; (x: number, y: null): void }, { (x: number, y: null): void; (x: 0, y: null): void }> // false

But function type intersection does not work:

type OF2 = EqEqEq<F & G, G & F> // true
AnyhowStep

AnyhowStep commented on Mar 10, 2020

@AnyhowStep
Contributor

Can we re-open this or something? Because there isn't a way =(

AnyhowStep

AnyhowStep commented on Jun 15, 2020

@AnyhowStep
Contributor

#37314 (comment)

Seems like it's fixed in master, with TS 4.0 as the milestone.

So, this doesn't need to be re-opened, I suppose. I forgot to link to that comment sooner, my bad.

127 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

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @gfx@weakish@ecyrbe@guiguan@DanielRosenwasser

        Issue actions

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