Reputation: 21380
I want to check if 2 types are the same one. I expected
export type AreSame<T, U, R> = T extends U ? U extends T ? R : never : never;
to work, but actually it fails on unions:
type A = AreSame<1, 1, Object> // Object
type B = AreSame<1, 2, Object> // never
type C = AreSame<1, number, Object> // never
type D = AreSame<1, 1 | 2, Object> // Object - why???
type E = AreSame<1 | 2, 1 | 3, Object> // Object - why???
type F = AreSame<1 | 2, 1 | 2, Object> // Object
How can I fix that?
Upvotes: 1
Views: 731
Reputation: 317
I am just taking a guess here, but if we consider a union to be similar to a logical or, this would make sense.
Let me try to pseudo code the steps it takes for the question..
1 extends (1|2) ? (1|2) extends 1 ? Object : never : never // becomes ->
(1 extends 1 || 1 extends 2) ? (1 extends 1 || 2 extends 1) ? : Object : never : never
I would consider checking the typescript code to check how it exactly works..
EDIT: To solve this problem, I found this github issue in the typescript repository
The best solution there is this:
export type Equals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false;
Maybe you can adjust that for your case? Hope that helps :)
Upvotes: 4
Reputation: 327944
Your conditional types are accidentally distributive. That means it ends up splitting up the unions, evaluating the type for each member, and unioning them back together. Please familiarize yourself with that documentation, because it's important. If you have a type like type G<T> = T extends U ? X : Y
where T
is a "naked" type parameter (so it's T extends
and not SomethingWith<T> extends
) then it will distribute. Sometimes this is what you want. Right now it's not.
The easiest way to turn off distributive conditional types is by "clothing" the naked type parameter: turn T extends U ? X : Y
into [T] extends [U] ? X : Y
. So:
export type AreSame<T, U, R> = [T] extends [U] ? [U] extends [T] ? R : never : never;
type A = AreSame<1, 1, Object> // Object
type B = AreSame<1, 2, Object> // never
type C = AreSame<1, number, Object> // never
type D = AreSame<1, 1 | 2, Object> // never
type E = AreSame<1 | 2, 1 | 3, Object> // never
type F = AreSame<1 | 2, 1 | 2, Object> // Object
Okay, hope that helps; good luck!
Upvotes: 5