You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
extends converts type of the form A | B to A & B #36445
Code
Key parts to look at in this code are the definition of funcA which specifies the type of the argument to be A | B, but since it is typed as Slowboy<Payload> it goes through an extends undefined check which changes the expected argument type of funcA from A | B to A & B.
typeA={isNew: true;extra: {name: string,age: number}}typeB={isNew: false;}typeAorB=A|BtypeData<T>={name: string;payload: T}typeSlowBoy<Payload>=Payloadextendsundefined ?
(payload?: undefined)=>Data<undefined> :
(payload: Payload)=>Data<Payload>;constfuncA: SlowBoy<AorB>=(payload: AorB)=>({name: "A",
payload
})funcA({//Shows the expected type of argument to funcA as A & BisNew: true,extra: {name: 'ijxsid',age: 310}})
Expected behavior:
I expect the extends keyword to check if the Payload type extends undefined and if its the second case in which the Payload doesn't extend undefined, the Payload type should remain as applied in the definition of funcA, that is A | B.
Actual behavior:
The Payload type applied in the definition of funcA gets modified by the extends type check from A | B to A & B.
Is this the expected behavior of extends? Cause I don't think the type checks done by extends undefined should stay after the check has already determined the type.
The type SlowBoy<AorB> is the union ((payload: A) => Data<A>) | ((payload: B) => Data<B>) because SlowBoy<> is a distributive conditional type so SlowBoy<A|B> is equivalent to SlowBoy<A> | SlowBoy<B>. And as of TS3.3, a union of functions can be called with an intersection of the parameters.
Activity
jcalz commentedon Jan 27, 2020
This is working as intended, as per #29011. See the documentation for improved behavior for calling union types.
The type
SlowBoy<AorB>is the union((payload: A) => Data<A>) | ((payload: B) => Data<B>)becauseSlowBoy<>is a distributive conditional type soSlowBoy<A|B>is equivalent toSlowBoy<A> | SlowBoy<B>. And as of TS3.3, a union of functions can be called with an intersection of the parameters.AlCalzone commentedon Jan 27, 2020
You need to stop the type argument from being distributed by wrapping it in a tuple:
This works:
https://www.typescriptlang.org/play/#code/FAFwngDgpgBAgjAvDA3sGGYEsDOA5KAdwC4YQAnAVygG51MoAPCgQ1LU05gDsWBbKKRwUs3AOYAaelxgsxgnpT4AjKOWkwAvsG2hIsAEJJUG3ARIwAZiwA2OWjuB7o8APbkjyBAB8YBp+AuACIsICwAPAAqAHzGHJi8AkIi4nScECxgNq4sACakkTCOzrAAytmEBq5g4QAKmdl5scgA2vVZObkAujBMIFDcuTgwLZSDUJaiUN0wAPwaABQZHXmzpGO5E1O5AJRIsSFh4Rtb3NOxxIvLjfkw7Td7iAehEfed0XROAMau3MJWYy+cFI5VclWq4Tg7gMzRgSwanVIUI8j1iC3iGESCgARHBsVJ0gi8jodk5LIC4OjTPgiKQKNQCQxmOQ2CYZJj+AoAORYABWjBwWFyXMZMjkCgAzABGAAMGm0mlJwB+fxAAO4XwMIIqVRqJ0mZ1ysIWqLhKB4nNI2Msrlc+Jg10RMH12y0OxoQA
ijxsid2 commentedon Jan 27, 2020
Thanks AlCalzone, that saves it from converting from
A | BtoA & B. I will close the issue unless someone else wants to comment.