Skip to content
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

"Types .. have no overlap" (2367) error when LHS and RHS generics both extend same constraint #51584

Closed
yseymour opened this issue Nov 18, 2022 · 6 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@yseymour
Copy link

yseymour commented Nov 18, 2022

Bug Report

πŸ”Ž Search Terms

2367 types overlap

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

interface Base { x: number }

class C<T extends Base> {
    constructor(readonly x: T) {}

    foo<U extends Base>(y: U) {
        if (y === this.x) {}  // <-- error: 'U' and 'T' have no overlap
    }
}

πŸ™ Actual behavior

An error is raised on the if clause.

This comparison appears to be unintentional because the types 'U' and 'T' have no overlap.(2367)

πŸ™‚ Expected behavior

Both U and T are constrained by Base, so they conceivably could overlap, so comparing them should be OK (and as a side-effect, it'd be nice if the comparison also refined U to T inside the if block).

maybe related: #50893 (PR: #50978)

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Nov 18, 2022
@RyanCavanaugh
Copy link
Member

so they conceivably could overlap

This is always going to be true, since all types overlap at unknown. Equality checks are necessarily incomplete and while this one is on the border, I think being a bit safer is the preferred direction to go.

@fatcerberus
Copy link

Hmm, this seems like a duplicate of #50893 which is tagged as a bug. @RyanCavanaugh, thoughts?

@RyanCavanaugh
Copy link
Member

The difference is between foo<U extends Base> vs foo<U extends T> (in the context of this example)

@yseymour
Copy link
Author

yseymour commented Nov 19, 2022

all types overlap at unknown.

Agreed, but that's materially different to this case where we have two types that are related by a common constraint, isn't it?

I guess I can't see a reason for this error in cases where the common supertype is known to be neither any nor unknown.

Let me give you an example that's closer to my actual code:

enum Model { User, Group }

interface User { name: string, group: Group }
interface Group { name: string }

type RecordType = {
    [Model.User]: User;
    [Model.Group]: Group;
}

class View<T extends Model> {
    constructor(
        readonly primaryModel: T
    ) {}

    consume<U extends Model>(model: U, record: RecordType[U]) {
        if (model === this.primaryModel) {  // <-- error
            this.consumePrimaryModel(record);
        }
        else {
            this.consumeForeignModel(record);
        }
    }

    consumePrimaryModel(record: RecordType[T]) {}
    consumeForeignModel(record: RecordType[Model]) {}
}

(Playground)

A View can be instantiated for different kinds of Model, and needs to be able to consume records of either its primary model, or a foreign dependency. I want to dispatch based on the kind of model that was received.

As written, the code feels correct, but doesn't type-check without additional assertions, which feels clunky (and IMO less safe, since it's possible to get the type assertion wrong).

@fatcerberus
Copy link

that's materially different to this case where we have two types that are related by a common constraint, isn't it?

Not really. Even "unconstrained" type parameters are implicitly constrained by unknown, and in that case it's usually clear that comparing them is a bug (if you wanted the types to be correlated you would've either derived them from a single type parameter or else have the second be constrained by the first). So clearly "having a common constraint" can't be a sufficient condition for comparability and it then becomes a tricky subjective question of where to draw the line.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants