Skip to content

async type narrowing #62624

Closed as duplicate
Closed as duplicate

Description

🔎 Search Terms

"async", "Promise", "Promise<never>", "never", "type narrowing"

🕗 Version & Regression Information

  • This is the behavior in every version I tried (incl. 5.9.3, nightly), and I reviewed the FAQ for entries about async, Promise<never>, narrowing

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.3#code/GYVwdgxgLglg9mABAcwKZQGoEMA2JUAUAlAFyIDOUATjGMogD6Jgg46IDeAUIr4lehBUkAIigALVNjyoRXAL5cuWcgE9IiUJFgJEAEzgB1OFQDWxMgAUqcALYxyqADwA3ODD0A+TokVcA9ABUgTyBiAAqqgAOqAC0YFhUNgDutPQOiFhRUTgwqHoAdIgABgAexRRYsOTAeeSIEqiIEAiUVFi0UCVtacUFof5cWtDwSAYAynbo4mnj6hAWFNRpnDx8wzpIwB04i2CoLqhUq3ynDeIpzKjJiACiSSbEa7x+py1glIilZD10jMysdgAXhQ6Gk+CepxgwEQBFKiCBiIBbCImh2kL4AigQiQ8PIVQctVQ9V+yAUSiCIUQYUiMXiiRSKwyWRyeUKJXKlWqRPqjWarWoHTAXWKpL6A2Uag0G1G+jgk1s0zSAEEpRBDDAJABJMC5fYAMXRpEQ1jsDmcpO83DeAq+P2WfyYLDYCNBmFwEKIz0Q0Nh8MRIOdOFR1rOvCwyQ6XQMxjMGLDEku+xu9xsVHjvm9WJxXy5hLqSxodHJAWCoQi0TiCQeqT+GTAcC6LNy+SKZQqBmJzEbeZqqnOTXebSFIrFmXqmsQtiw-YARk04DCoJWSkHxdTBip5ppwCNdBMphIVWqNRJVfN9bvNoaYLtjab7I4nJaTnwt9Kr7LtrfFg-zU59kOKgrW9U4IyjOVY3ML0w14RM4BuZM7gedMYL4V4+CHLpvkLFYnUBV00HdGQM19OEESRINUXAydvzvLNBGEXN8W5AtSXJIA

💻 Code

function getValue(): string | null {
    return "theValue"
}

async function doWork(): Promise<void> { }

/**
 * Type-narrowing is applied. `x` satisfies the constraint `string`.
 */
function doSomethingSync(): string {
    function fail(): never {
        throw new Error()
    }

    const x: string | null = getValue()
    if (x === null) fail()
    return x satisfies string
}

/**
 * Type-narrowing is applied. `x` satisfies the constraint `string`.
 */
async function doSomethingAsyncWithInlineFail(): Promise<string> {
    const x: string | null = getValue()
    if (x === null) {
        await doWork()
        throw new Error()
    }
    return x satisfies string
}

/**
 * Type-narrowing is not applied. `x` does not satisfy the constraint `string` as it may be of type `null`.
 */
async function doSomethingAsyncWithAsyncFunctionFail(): Promise<string> {
    async function fail(): Promise<never> {
        await doWork()
        throw new Error()
    }

    const x: string | null = getValue()
    if (x === null) await fail()
    return x satisfies string
}

🙁 Actual behavior

doSomethingAsyncWithInlineFail and doSomethingAsyncWithAsyncFunctionFail behave differently - the former applies type-narrowing, the latter does not.

🙂 Expected behavior

doSomethingAsyncWithInlineFail and doSomethingAsyncWithAsyncFunctionFail should behave identically.
My personal preference would be to apply the behaviour of doSomethingAsyncWithInlineFail to doSomethingAsyncWithAsyncFunctionFail.
However, I understand this might not be technically correct as the event loop might execute code which mutates the type-narrowed value.

Additional information about the issue

No response

Activity

  1. RyanCavanaugh commented on Oct 17, 2025

    @RyanCavanaugh
    Member

    Duplicate #34955

  2. Lordfirespeed commented on Oct 18, 2025

    @Lordfirespeed
    Author

    Duplicate #34955

    Thankyou for this - I'm sorry I didn't find it, I thought I'd done my due diligence 😭

  3. locked as resolved and limited conversation to collaborators on Jan 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions