Provide flattened.error.reporting(...).for(...).nested.incompatible.types #33361
Copy link
Copy link
Closed
Closed
Copy link
Labels
Domain: Error MessagesThe issue relates to error messagingThe issue relates to error messagingEffort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Experience EnhancementNoncontroversial enhancementsNoncontroversial enhancementsFixedA PR has been merged for this issueA PR has been merged for this issueSuggestionAn idea for TypeScriptAn idea for TypeScript
Milestone
Description
let x = { a: { b: { c: { d: { e: { f() { return { g: "hello" }; } } } } } } };
let y = { a: { b: { c: { d: { e: { f() { return { g: 12345 }; } } } } } } };
x = y;Existing Behavior
Type '{ a: { b: { c: { d: { e: { f(): { g: number; }; }; }; }; }; }; }' is not assignable to type '{ a: { b: { c: { d: { e: { f(): { g: string; }; }; }; }; }; }; }'.
Types of property 'a' are incompatible.
Type '{ b: { c: { d: { e: { f(): { g: number; }; }; }; }; }; }' is not assignable to type '{ b: { c: { d: { e: { f(): { g: string; }; }; }; }; }; }'.
Types of property 'b' are incompatible.
Type '{ c: { d: { e: { f(): { g: number; }; }; }; }; }' is not assignable to type '{ c: { d: { e: { f(): { g: string; }; }; }; }; }'.
Types of property 'c' are incompatible.
Type '{ d: { e: { f(): { g: number; }; }; }; }' is not assignable to type '{ d: { e: { f(): { g: string; }; }; }; }'.
Types of property 'd' are incompatible.
Type '{ e: { f(): { g: number; }; }; }' is not assignable to type '{ e: { f(): { g: string; }; }; }'.
Types of property 'e' are incompatible.
Type '{ f(): { g: number; }; }' is not assignable to type '{ f(): { g: string; }; }'.
Types of property 'f' are incompatible.
Type '() => { g: number; }' is not assignable to type '() => { g: string; }'.
Type '{ g: number; }' is not assignable to type '{ g: string; }'.
Types of property 'g' are incompatible.
Type 'number' is not assignable to type 'string'.
Proposed
Type '{ a: { b: { c: { d: { e: { f(): { g: number; }; }; }; }; }; }; }' is not assignable to type '{ a: { b: { c: { d: { e: { f(): { g: string; }; }; }; }; }; }; }'.
'a.b.c.d.e.f(...).g' is incompatible between these types because 'number' is not assignable to type 'string'
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Domain: Error MessagesThe issue relates to error messagingThe issue relates to error messagingEffort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Experience EnhancementNoncontroversial enhancementsNoncontroversial enhancementsFixedA PR has been merged for this issueA PR has been merged for this issueSuggestionAn idea for TypeScriptAn idea for TypeScript
Activity
orta commentedon Sep 11, 2019
I explored this a bit with Eloy Durán (@alloy) when thinking about whether we could also use bold/italics to also provide emphasis, instead we used
{ ..., b: { ... c: } }to provide focus while keeping the pyramid of nested errors.I prefer the version in OP. I wonder if turning it into three lines would make it more readable:
Then you get to see the full message without scrolling, and can more easily compare the types.
Ideally, if we're in an environment where we can use bold:
Which would really help lead someone's eyes to the right spots to make their own decisions.
dhruvrajvanshi commentedon Sep 11, 2019
I'd like to try this.
DanielRosenwasser commentedon Sep 11, 2019
Dhruv Rajvanshi (@dhruvrajvanshi) Go for it! It might be a little bit tricky because this is deep in the logic for
checkTypeRelatedTo.To scope the problem space, I'll call the special diagnostic
a member chain diagnostic.
A member chain diagnostic is created by tracking checks between properties and call signatures, and determining if multiple relevant properties/call signatures are encountered consecutively. A member is relevant for these checks if the member is
Symbol.iteratoror"hello there"){ (): number; (x: any): string }and() => booleanshouldn't be included)I like that Orta Therox (@orta). Maybe that can be layered on top for the first message, even if we can't do styling on diagnostics yet. I'd open up a separate issue if you're interested!
DanielRosenwasser commentedon Sep 11, 2019
(Please forgive me for my poorly written spec 😅)
[-]Provided flattened.error.reporting(...).for(...).nested.incompatible.types[/-][+]Provide flattened.error.reporting(...).for(...).nested.incompatible.types[/+]DanielRosenwasser commentedon Sep 13, 2019
By the way, we'd like to get this in for the beta if possible. Do you think you can get this done by the end of the month Dhruv Rajvanshi (@dhruvrajvanshi)?
dhruvrajvanshi commentedon Sep 14, 2019
Daniel Rosenwasser (@DanielRosenwasser) In that case, you might want to assign this to someone else. I can't really commit to a deadline right now.
austincummings commentedon Sep 15, 2019
Daniel Rosenwasser (@DanielRosenwasser) I think I could commit to getting this done by then. Is there someone/somewhere in particular I should ask for guidance on this? Or just here on the issue?
orta commentedon Sep 16, 2019
I think this issue is a good place to leave notes and questions 👍
austincummings commentedon Sep 17, 2019
Just wanted to give a small update and see if I'm on the right approach.
So far I've mostly just been getting an understanding of how
checkTypeRelatedToworks and playing with how to track the property checks.Could this be done by keeping a linked list structure (or even just an array) defined in
checkTypeRelatedTo, and appending thetargetPropSymbolinpropertyRelatedTowhen it reports theDiagnostics.Types_of_property_0_are_incompatibleerror? (maybe also include a reference to the currenterrorInfoso I can match the message chain element and the member chain element?).Assuming that is correct, and once we have a "member chain" built, before
createDiagnosticForNodeFromMessageChainis called is where I was thinking I could "collapse" theerrorInfodown into the desired diagnostic (which I haven't yet considered how to do.).Hopefully I'm on the right track, and I may open a draft PR if that is ok to sort of show the work being done.
weswigham commentedon Sep 17, 2019
Austin Cummings (@austincummings) sorry my friend, but as of earlier today, we have a pretty much complete implementation that we should have a PR up for by EoD tomorrow - the assignment was supposed to indicate that~
Hopefully it'll at least be educational? 🤷♀
austincummings commentedon Sep 17, 2019
Wesley Wigham (@weswigham) no worries. I'm sure it will be. Thanks for letting me know.
elektronik2k5 commentedon Sep 26, 2019
May I suggest a small whitespace tweak to make the new and awesome error a bit more readable?
Instead of
move the
is not assignable toto the end of the first line, so the types vertically align for easier scanning and vertical comparison:or, to minimize horizontal scrolling/overflow place it in a line in between the types:
Happy to make such a PR :)
weswigham commentedon Sep 26, 2019
Individual multiline diagnostic messages I think require a couple changes to our diagnostic message chain builder, but I'd be down for considering it, personally. It's worth remembering that it could still occur in a pyramid, so you could have something like
which is.... maybe better? I dunno, linebreaks, man. I know I probably don't wanna see
so there'd probably need to be some kind of length heuristic, at least.
karlhorky commentedon Sep 26, 2019
Brainstorming about the (currently) impossible here, I would say that the thing that
gitdoes on highlighting diffs (viadiff-highlight) would be super interesting here:Would the TypeScript team be open to a pipe dream issue about this for some future?
weswigham commentedon Sep 26, 2019
So while that's pretty ok in the extreme example from the op, most[citation needed] types involved in larger errors aren't deeply nested anonymous objects, but are instead interfaces within interfaces - so your top level type error is usually something like
A<Foo<Q>> is not assignable to B<Bar<U>>- so the "diff" is non-existent (since the whole thing is different). With this change and other changes we've made in the past (diving down to exact properties to issue errors on, for instance), I think we've got the anonymous object case handled pretty well now (at least enough that I think attempting to colorize textual diffs is only a marginal improvement (if at all - it also could border on misleading in some cases, like when structurally near-idential types with differing names are used) on the path callout on the following line) - it's the opaque class/alias (usually signature involving) structures that we really do not as much have down.karlhorky commentedon Sep 26, 2019
Ok, then I'll avoid creating a new issue for it. Thanks for the feedback!
karol-majewski commentedon Sep 26, 2019
It would be wonderful to have more approachable error messages in situations like this:
https://imgur.com/F4Ov3Z7
Here, the problem is related to index signatures. The expected type has one, the provided type doesn't.
In order to see what the real problem is, one needs to read a pretty big and intimidating error message in which the important message is on the bottom.
RyanCavanaugh commentedon Sep 26, 2019
Karol Majewski (@karol-majewski) "just print the error messages in reverse order" is a common suggestion; see #29759 (comment) . TL;DR it makes some things better and other things worse
karol-majewski commentedon Sep 27, 2019
Ryan Cavanaugh (@RyanCavanaugh) Thank you for your reply. Reversing the order is one dimension and I agree this is not a perfect solution everywhere.
What I had in mind was making the error message say which object is missing the index signature. Right now we only know one of them is missing it.
My hope is this could point the users into the right direction: finding a way to update the object in a way that preserves its index signature (which the spread operator
...doesn't do).