Skip to content

Intersection of multiple type guard function types (or multiple overloads) has inconsistently inferred predicate typesΒ #50708

Closed
@lazytype

Description

@lazytype

Bug Report

πŸ”Ž Search Terms

type guard assertion function intersection predicate

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about type guards

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type GuardIntersection = ((x: unknown) => x is number | string) &
    ((x: unknown) => x is number | boolean);

type AssertionIntersection = ((x: unknown) => asserts x is number | string) &
    ((x: unknown) => asserts x is number | boolean);

type GuardType = GuardIntersection extends (x: any) => x is infer U ? U : never;
//   ^?
//      type GuardType = number | boolean

type AssertionType = AssertionIntersection extends (x: any) => asserts x is infer U ? U : never;
//   ^?
//      type AssertionType = number | boolean

function example(guard: GuardIntersection, assertion: AssertionIntersection, x: unknown): number {
    if (guard(x)) {
        return x;
        //     ^?
        //        (parameter) x: string | number
    }

    assertion(x);
    return x;
    //     ^?
    //        (parameter) x: string | number
}

πŸ™ Actual behavior

When using infer to extract the predicate type of the intersection of type guard function types, the predicate type of the last item in the intersection is inferred. (number | string)
When calling a type guard function of the intersection type, the argument of the type guard function is narrowed to the predicate type of the first item in the intersection. (number | boolean)

πŸ™‚ Expected behavior

In both cases, I'd expect the inferred/narrowed type to be the intersection of the predicate types. (number)
At the very least I'd expect the two types to be the same.

Aside: This is remarkably similar to the behavior of inferring the return type of function overload types (#26591). Coincidence?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions