Typescript: await collapses union type

Created on 14 Aug 2017  路  4Comments  路  Source: microsoft/TypeScript



If one type in a union type extends the other, when awaiting a promise with this union type the least specific type is inferred.
In the code below a is of type Promise\b is of type A.

TypeScript Version: 2.4.1

Code

type A = { a: number }
type B = { a: number, b: number }
type C = A | B

async function f(): Promise<C> {
    throw ''
}

(async () => {
    const a = f()
    const b = await a
})()

Expected behavior:
b is of type C

Actual behavior:
b is of type A

Design Limitation

Most helpful comment

There maybe assignability, but there seems to be an issue:

type A = { a: number }
type B = { a: number, b: number }
type C = A | B

async function f(): Promise<C> {
    throw ''
}

function g(): C {
    throw ''
}

(async () => {
    const a = f() // type Promise<C>
    const b = await a // type A
    const c = g() // type C
    f().then((d) => { /* d type C */ })
})()

Only when await is used does the type change in a surprising way.

All 4 comments

This is presented a little strangely by the language service but I believe it is correct because
{ a: number } is equivalent to { a: number } | { a: number, b: number}

There maybe assignability, but there seems to be an issue:

type A = { a: number }
type B = { a: number, b: number }
type C = A | B

async function f(): Promise<C> {
    throw ''
}

function g(): C {
    throw ''
}

(async () => {
    const a = f() // type Promise<C>
    const b = await a // type A
    const c = g() // type C
    f().then((d) => { /* d type C */ })
})()

Only when await is used does the type change in a surprising way.

Union types of this form behave strangely because we don't "realize" that they're collapsible until we actually go and try to do something with them (e.g. map into a promise return type).

In an ideal world it would just not be legal to write a union type like this, but generic instantiation means there's not always a recognizable line of code associated with the declaration of such a type. For the time being, just... don't write types like that.

Just to be explicit, I think you are pointing out that:

type C = { a: number } | { a: number; b: number; };

is a _silly_ type (that ideally would be illegal) that causes issues in certain constructs and that really, it should be written as:

type C = { a: number; b?: number; };

and then there would be no issues.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

OliverJAsh picture OliverJAsh  路  242Comments

Gaelan picture Gaelan  路  231Comments

nitzantomer picture nitzantomer  路  135Comments

rwyborn picture rwyborn  路  210Comments

tenry92 picture tenry92  路  146Comments