microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.75k stars 12.46k forks source link

Misleading error message #56230

Open Nagyjano opened 12 months ago

Nagyjano commented 12 months ago

🔎 Search Terms

Misleading error message

🕗 Version & Regression Information

I think in all version but I just tries 5.2.2 and 5.3.0-beta

⏯ Playground Link

https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgPIAczAPYgDwAqAfMgN4CwAUMjcsJALYD8AXMgQNxW3IzAQAbACYBnVsgAUAawgBPbDHYBKDgHpVyAK4jQAc2QADEWCh6A2gF0DyOCORhZ6FAAtoKAO7ZNw5AzgzkdGwRHQAjARQwV3swMBsQ4F0QBghwKgBfKgQBWzsCCGMAIVsIQmQIAA9IEFFkbFCAKwgEOIBeOsbmsBIKalpIYwkwNgwsXEIiJTJMyhnQSFhEFAARODA4Mm5aITW4NmNTEF0uWayckPYCsABhZ2AfSura-KKSvFX1nq2aAbAhkcwOHwHzgk2mGSoVAicViw0urxEKHaIAg7nhNzuwgkKiAA

💻 Code

interface Option<T> {
    item?: T;
    fields?: (keyof T);// using `string[]` as type here would make possible the ttt assignment
}
class TestBase<T extends object = object> {
    test(t: Option<T>) {}
}
interface Data {
    data: string;
}
class TestChild extends TestBase<Data> {
    test(t: Option<Data>) {}
}

let ttt: TestBase = new TestChild();

🙁 Actual behavior

In the error message if I hover over ttt it states that the item member is incompatible, but I think it is not the problem but the fields member. If I change its type from (keyof T)[] to type string[] the error is solved but as I see this means the error message was misleading.

🙂 Expected behavior

The error message should state that the fields member is the problematic

Additional information about the issue

I think the problem is that fields member has type keyof object in base class (TestBase) which maybe results in type never and in derivative class (TestChild) the fields results in "data" which is defineatly not assignable to never type. Although I have years of experience in ts it can surprise me sometimes. :)

ps: Sorry if it is a known issue or expected behavior. Then please point me out to the corresponding documentation

Andarist commented 12 months ago

By using keyof T you make T contravariant there (can be tested by removing item property and adding in annotation to T). But at the same time T is covariant based on the item property - so all in all, T becomes invariant. TypeScript can't know which one you have actually wanted here - so it errors on the first incompatible property based on the variance measurement

RyanCavanaugh commented 11 months ago

It's not clear to me there's a good way to improve this, but someone can try. Option<T> as written is invariant on T, so we're doing a bidirectional assignability check and failing as soon as that fails. It's confusing to see the contravariance-based error on the covariant property to be sure, but arguably the "right" fix for that problem is to simply stop elaborating - probably not an improvement. Scouring the type for a contravariant usage instead is also arguably worse, since it's more confusing to talk about a keyof S vs keyof T mismatch when we could just be talking about S and T directly.