Skip to content

Question: How does UnionToIntersection helper work #27907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
cevek opened this issue Oct 15, 2018 · 3 comments
Closed

Question: How does UnionToIntersection helper work #27907

cevek opened this issue Oct 15, 2018 · 3 comments

Comments

@cevek
Copy link

cevek commented Oct 15, 2018

Can anybody to explain how does this black evil magic work?

type UnionToIntersection<U> =
	(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

If I simplify (U extends any ? (k: U) => void : never) -> (a: U)=>void)
it starts to work just as expected (No intersection)

type UnionToIntersection<U> = ((a: U)=>void) extends ((k: infer I) => void) ? I : never
type F = UnionToIntersection<'A' | 'B'> // A | B

Distributive conditional types iterate over union and apply all union units to this condition
But somehow this condition (U extends any ? (k: U) => void : never) tells ts that we don't need to iterate over union items in the outer extends but in the inner extends and make new type: ((k: 'A') => void) | ((k: 'B') => void)

so after all this combinations ts would work with type like this:

type H = (((k: 'A') => void) | ((k: 'B') => void)) extends (k: infer I)=>void ? I : never;

And return 'A' & 'B' because function argument is contra-variant

So question why outer extends doesn't iterate union?

@jack-williams
Copy link
Collaborator

jack-williams commented Oct 15, 2018

The check type in the outer conditional is (U extends any ? (k: U) => void : never). Conditional types only distribute if the check type is a naked type parameter like U.

@cevek
Copy link
Author

cevek commented Oct 15, 2018

Aa, I understand, if we have some calculated type ts doesn't iterate the union

type JoinReturnType<T> = T & {} extends ()=>infer Ret ? () => Ret : never;
type A = () => number;
type B = () => string;
type U = JoinReturnType<A | B>;  // () => string | number
// without & {} hack it will be () => string | () => number

@cevek cevek closed this as completed Oct 15, 2018
@jack-williams
Copy link
Collaborator

Yep! I think the advised way to 'turn-off' distribution, or iteration, is to use a tuple type:

type JoinReturnType<T> = [T] extends [() => infer Ret] ? () => Ret : never;

Your approach works here but there might be some issues in the propagation of constraints (I'm not sure).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants