Typescript: TS80007: Async function response arguments not inferred

Created on 26 Jul 2019  路  6Comments  路  Source: microsoft/TypeScript


TypeScript Version: [email protected]


Search Terms: TS80007 as of #32384

Code

type MaybePromise<T> = T | Promise<T>

type StatefulModifier<S> = MaybePromise<(arg: S) => number>

type CountActions<S = { count: number }> = Record<string, 
    (...args: any[]) => StatefulModifier<S>
>

const actions: CountActions = {
    // works fine, `state` arg correctly inferred
    increment: (toAdd: number) => {
        return (state) => state.count + toAdd
    },

    // works fine, `state` arg correctly inferred from a promise.resolve res
    first: () => Promise.resolve(mockedPromise()).then((res) => {
        return (state) => state.count + res
    }),

    // works fine, `state` arg correctly inferred from a new promise
    second: () => new Promise((res) => {
        res((state) => state.count + 50)
    }),

    // fails, `state` arg as `any` and TS warning 80007 on async
    third: async () => {
        const res = await mockedPromise()

        // arg type inferred as any
        return (arg) => {
            return 10 * res
        }
    },
}

Expected behavior:

The promised function signature of the third object property should be inferred in the same respect as the previous two promised functions.

Actual behavior:

Typescript is able to infer the promised function signature when it's provided through => Promise.resolve or => new Promise(... but unable to do so when supplied as a result of an async function.

However, the inference resolves correctly when the MaybePromise union type is just simply a promise:

type MaybePromise<T> = Promise<T>

Playground Link: Click me

Bug Rescheduled

Most helpful comment

I think we need to update the contextual typing rules for async functions to include the case where the contextual type is a union where one or more constituents is a Promise type

Simplified

type MaybePromise<T> = T | Promise<T>;
type NumberToNumber = (arg: number) => number;
type CountActions = Record<string,  () => MaybePromise<NumberToNumber>>;

const actions: CountActions = {
    // OK
    increment: () => (state) => state,

    // OK
    first: () => Promise.resolve(mockedPromise()).then((res) => 
        (state) => state + res
    ),

    // OK
    second: () => new Promise((res) => {
        res((state) => state)
    }),

    // fails, arg is `any`
    third: async () => {
        return (arg) => {
            return 10
        }
    },
}

const mockedPromise = () => new Promise<number>(res => {
    setTimeout(() => res(10), 1200)
})

All 6 comments

I think we need to update the contextual typing rules for async functions to include the case where the contextual type is a union where one or more constituents is a Promise type

Simplified

type MaybePromise<T> = T | Promise<T>;
type NumberToNumber = (arg: number) => number;
type CountActions = Record<string,  () => MaybePromise<NumberToNumber>>;

const actions: CountActions = {
    // OK
    increment: () => (state) => state,

    // OK
    first: () => Promise.resolve(mockedPromise()).then((res) => 
        (state) => state + res
    ),

    // OK
    second: () => new Promise((res) => {
        res((state) => state)
    }),

    // fails, arg is `any`
    third: async () => {
        return (arg) => {
            return 10
        }
    },
}

const mockedPromise = () => new Promise<number>(res => {
    setTimeout(() => res(10), 1200)
})

I have a bug to report, I'll leave this here because it seems related but I'm not sure if it's TypeScript or VSCode responsiblity, since this is just JavaScript in VSCode using JSDocs/ESDocs. But I guess VSCode uses TypeScript to infer types? Please let me know if I should report this in VSCode instead.

x is inferred with any instead of null:

 /**
  * @returns {Promise<null>} 
  */
 async method(listener) { }

 var x = await method(); // x is 'any' but it should be null

It only fails for null or undefined. It works for other types.

We are hitting this bug in our type definitions for Fastify. See: https://github.com/fastify/fastify/pull/2350

Is there a reason this keeps getting bumped the next version of TS? Anything we can do to help solve it? Is there a recommended workaround?

This issue is fixed in 3.9 judging on both of the playgrounds - @Ethan-Arrowood you might be looking at a different bug

@orta please have a look at this playground I've created. Am I doing something wrong?

~Ahh taking a second look at this issue it seems its more about whats returned from the async functions. But the question still stands for why my playground is not working... should I open a new issue for it?~

I realize my mistake now keep this closed; not a bug with TS

Was this page helpful?
0 / 5 - 0 ratings