T1 == T2
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.
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
My suggestion meets these guidelines:
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
.
@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
any
is not assignable to never
, so you should be able to determine whether or not either side is exclusively any
.
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
.)
Thank you
There was a way
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.
@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:
@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
Can we re-open this or something? Because there isn't a way =(
https://github.com/microsoft/TypeScript/issues/37314#issuecomment-598459316
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.
@jituanlin AFAIK it relies on conditional types being deferred when
T
is not known. Assignability of deferred conditional types relies on an internalisTypeIdenticalTo
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
where can I find the infomations about the internal 'isTypeIdenticalTo' check? I can't find anything in the typescript official website....
@ldqUndefined I do not remember it well, but you may find it in the source code (or not, since TypeScript source code changed a lot).
Most helpful comment
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: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
.)