Skip to content

extends converts type of the form A | B to A & B #36445

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
ijxsid2 opened this issue Jan 27, 2020 · 3 comments
Closed

extends converts type of the form A | B to A & B #36445

ijxsid2 opened this issue Jan 27, 2020 · 3 comments

Comments

@ijxsid2
Copy link

ijxsid2 commented Jan 27, 2020

TypeScript Version: 3.7.2

Search Terms: extends, type inference

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.

type A = {
    isNew: true;
    extra: {
        name: string,
        age: number
    }
}

type B = {
    isNew: false;
}

type AorB = A | B

type Data<T> = {
    name: string;
    payload: T 
}

type SlowBoy<Payload> = Payload extends undefined ?
    (payload?: undefined) => Data<undefined> :
    (payload: Payload) => Data<Payload>;


const funcA: SlowBoy<AorB> = (payload: AorB) => ({
    name: "A",
    payload
})

funcA({ //Shows the expected type of argument to funcA as A & B
    isNew: 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.

Playground Link: https://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=37&pc=3#code/FAFwngDgpgBAgjAvDA3sGGYEsDOA5KAdwC4YQAnAVygG51MoAPCgQ1LU05gDsWBbKKRwUs3AOYAaelxgsxgnpT4AjKOWkwAvsG2hIsAEJJUG3ARIwAZiwA2OWjuB7o8APbkjyBAB8YBp+AuACIsICwAPAAqAHzGHJi8AkIi4nScECxgNq4sACakkTCOzrAAytmEBq5g4QAKmdl5scj1WTm5MEwgUNy5ODCUvVCWolAdAPwaABQZbXnjpIO5w6O5AJRIsSFh4Usr3GOxxNOzjfkwrWcbiFuhEZft0XROAMau3MJWgy9wpOWulWq4Tg7gMzRgMwa7VIII811iU3iGESCgARHBUVJ0lC8jo1k5LN84IjTPgiKQKNQsQxmOQ2CYZMj+AoAORYABWjBwWFyLOpMjkCgAzABGAAMGm0mjWQA

Hover over the funcA call and it shows the expected argument type to be A & B

Related Issues:

@jcalz
Copy link
Contributor

jcalz commented 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>) 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.

@AlCalzone
Copy link
Contributor

@ijxsid2
Copy link
Author

ijxsid2 commented Jan 27, 2020

Thanks @AlCalzone, that saves it from converting from A | B to A & B. I will close the issue unless someone else wants to comment.

@ijxsid2 ijxsid2 closed this as completed Jan 27, 2020
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

3 participants