Skip to content

the types 'T' and 'typeof Class' have no overlap #44645

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

Open
karikera opened this issue Jun 18, 2021 · 8 comments
Open

the types 'T' and 'typeof Class' have no overlap #44645

karikera opened this issue Jun 18, 2021 · 8 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@karikera
Copy link

Bug Report

πŸ”Ž Search Terms

template class no overlap label:Bug

πŸ•— Version & Regression Information

4.3.4 / 4.4.0-dev.20210617 / Nightly Playground

⏯ Playground Link

Playground Link

πŸ’» Code

class Class {
    dummy = 0;
}


function test<T>(a:T):void{
    if (a === Class) {} // This condition will always return 'false' since the types 'T' and 'typeof Class' have no overlap.ts(2367)
}

πŸ™ Actual behavior

ts2367 error. a can be Class

πŸ™‚ Expected behavior

no error. a can be Class

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

Not all comparisons are allowed; you can upcast either side to unknown if this was intentional.

@karikera
Copy link
Author

karikera commented Jun 19, 2021

Thanks for replying.
I'm reducing it from my use cases. but it seems I reduced it too much without knowing it's intentional work

class Class {
}

class Class2 extends Class {
    dummy = 0;
}

class Class3 {
}

function test<T extends typeof Class>(a:T):void{
    if (a === Class) {} // it's okay
    if (a === Class3) {} // it's okay
    if (a === Class2) {} // it has problem
}

const test2:typeof Class = Class2; // it's okay

Playground Link

on this case. it has the same problem.
I thought this should be possible. but it also occurred in overall versions.

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript and removed Working as Intended The behavior described is the intended behavior; this is not a bug labels Jun 21, 2021
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jun 21, 2021
@RyanCavanaugh
Copy link
Member

That's sort of weird

@ssalbdivad
Copy link

I've noticed another similar scenario where TS incorrectly asserts two types have no overlap and complains about their comparison:

class Foo<T extends true> {
    declare t: T

    hasKind<kind extends true> (kind: kind) {
        // This comparison appears to be unintentional because the types 'T' and 'kind' have no overlap.
        return this.t === kind
    }
}

TS Playground

Amusingly in this case, this.t and kind are guaranteed to be reference equal.

Various manifestations of this error come up fairly often in my experience with generic parameters. This feels like the kind of message where TS should err on that side of caution, as it's not assignment- it's the language going out of its way to say "hey, this should never be possible, double check what you've done," which should really only be happening when there is certainty that the two types being compared are disjoint.

@RyanCavanaugh
Copy link
Member

TS should err on that side of caution

Isn't that what's happening, though?

@ssalbdivad
Copy link

TS should not prompt a developer to double check their logic based on a comparison between two values unless their types form a disjoint.

In the example I've highlighted, the two values are guaranteed to be equal based on their types, yet TS complains they have no overlap.

The more general case is if the parameters are unconstrained, i.e. TS has no information about their relationship to one another, you get the same message:

class Foo<T> {
    declare t: T

    hasKind<kind>(kind: kind) {
        // This comparison appears to be unintentional because the types 'T' and 'kind' have no overlap.
        return this.t === kind
    }
}

Two unknown values should certainly be comparable.

@RyanCavanaugh
Copy link
Member

I don't know what to say except that this is super, super intentional, and you really should get warned here. These are the kinds of checks where when we tighten them even slightly, we find huge numbers of actual bugs in real code.

There are all kinds of mistakes you can make with === which are not provably always-false but very unlikely to be intentional. Issuing a small number of false positives in weird code to get true positives in bad code is the trade-off that all typecheckers make on purpose.

@ssalbdivad
Copy link

ssalbdivad commented Apr 20, 2023

But in the example I mentioned, there is absolutely no reason to believe that comparison is unintentional.

Comparing two values of type unknown is of course legal. This scenario is completely equivalent from TS's perspective based on this context. You can constrain the generics however you want, e.g. both to number. Same result- TS won't let you compare the two numbers for equality.

I know often the === warnings are useful and I'm not suggesting that the majority of them are wrong or unhelpful- usually when TS claims the operands have no overlap, it's because they don't.

That said, there is something that occurs when comparing two generics like this that results in TS making an overtly false claim that it has no evidence to make- i.e. that those types have no overlap.

The only time this error should happen when comparing two generics like these are when both are constrained and the types of the two constraints form a disjoint. Other than that, it's both factually and practically wrong to assume the comparison is unintentional.

I originally encountered this while writing a very uninteresting snippet of code to check if a Node type had a certain constraint kind, represented as a string. Despite both generics being constrained to the exact same type (in this case, the union "domain" | "class" | "value"), TS wouldn't allow me to compare them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants