Repository navigation
Circular mapped property is treated as missing when selecting contextual type from intersections #64534
Description
Activity
RyanCavanaugh commented
on Sep 29, 2026 MemberMore actionsI don't see a defect here. The inference is correct and sound. Concretely, why is the expected behavior better? Talking about the implementation details is confusing in this context without specifying exactly why you wanted it to happen differently.
- addedNeeds More InfoThe issue still hasn't been fully clarifiedThe issue still hasn't been fully clarified
on Sep 29, 2026 Andarist commented
on Sep 29, 2026 ContributorMore actionsI just skimmed through this... and I'm pretty tired to truly digest this long issue. That said, it sounds like there is some implementation defect in all of this. The
nameproperty is certainly not circular but internally it happens to be treated as such. Unless the same effect applies to other (non-reverse mapped) properties... but I have never seen internally anything like that, so I'm inclined to say this is somehow specific to reverse mapped type propertiesRyan Cavanaugh (@RyanCavanaugh) Thanks. For this particular repro, I agree that the inferred result is sound.
My concern is that two semantically different states are currently collapsed into the same "undefined" result:
- the property is actually missing
- the property exists, but its type is temporarily unavailable due to circular resolution
Looking at the history, I wonder if two separate design decisions became coupled unintentionally:
- No contextual types from circular mapped type properties #38653: avoid using a circular mapped property as a contextual type
- Fixed an issue with contextual type for intersection properties (take 2) #52095: if no concrete property type is available, consider index-info fallback
Before #52095, a circular property returned early and did not continue into index-signature lookup. After the refactoring, both cases produce the same result and therefore take the same fallback path.
So my main question is whether treating these two states identically was intentional, or whether this is an incidental interaction between the two designs.
- addedUnactionableThere isn't something we can do with this issueThere isn't something we can do with this issueand removedNeeds More InfoThe issue still hasn't been fully clarifiedThe issue still hasn't been fully clarified
on Oct 6, 2026 RyanCavanaugh commented
on Oct 6, 2026 MemberMore actionsBoth conditions need the same thing to happen, since in the first case it's not there and in the second case it would trigger an implicit any error to proceed. If these go through the same code path that's just code deduplication and if you want them to behave separately, you'll have to tease them apart again.
Without a concrete use case to address I don't think there's any action here.
🔎 Search Terms
circular mapped property contextual type intersection index signature reverse mapped type isCircularMappedProperty
🕗 Version & Regression Information
⏯ Playground Link
No response
💻 Code
🙁 Actual behavior
The call is accepted.
The inferred declaration is:
During contextual typing, lookup of the
nameproperty through the reverse-mappedMapped<T>constituent reaches the circular mapped-property guard, sogetTypeOfConcretePropertyOfContextualTypedoes not produce a contextual property type from that constituent.The intersection-specific contextual typing logic introduced in #52095 can then consider applicable index information from other constituents. In this example, the index signature from
Indexedprovides() => 0 | 1as the contextual type ofname.That contextual type preserves the returned
0as a literal type, which allows reverse inference to infer:and the call succeeds.
Before #52095, the same code reports TS2769 because no index-signature contextual type is supplied in this situation. As a result,
() => 0is inferred as() => number, which is not assignable to() => 0 | 1.There appear to be two potentially separate questions here:
nameproperty should reach the circular mapped-property guard at all.🙂 Expected behavior
I am not certain that the pre-#52095 TS2769 result is necessarily the desired behavior for this particular reproduction, since the currently inferred result is sound.
What seems unclear is whether the interaction between circular mapped-property handling and the index-info fallback introduced in #52095 is intentional.
In particular, these situations currently become indistinguishable to the subsequent intersection contextual-typing logic:
If those cases are intentionally meant to permit the same index-info fallback, then the current behavior is expected.
Otherwise, either:
The main question is therefore whether this interaction is intentional.
Additional information about the issue
I found this while investigating my PR #64502, which deals with a different circular contextual-typing issue involving static properties.
While tracing contextual property lookup and circularity handling, I noticed that this reproduction reaches a path where lookup of the reverse-mapped property is suppressed by the circular mapped-property check, after which the intersection logic introduced by #52095 uses an applicable index signature as the contextual type.
I narrowed the observable behavior change down to #52095.
Tested revisions:
6894ff7f38211589f0f41f8383727c57144857e1(parent of Fixed an issue with contextual type for intersection properties (take 2) #52095):reports TS2769
e6edc567a3946d203436265c5b525817fe2f708d(Fixed an issue with contextual type for intersection properties (take 2) #52095):no error
#38653 introduced the behavior where contextual typing does not attempt to resolve a mapped property when doing so would cause circular type resolution.
#52095 later refactored contextual property lookup for intersections. When
getTypeOfConcretePropertyOfContextualTypedoes not produce a type for a constituent, the intersection logic may consider applicable index information unless a concrete property type is found elsewhere.I instrumented the current checker locally and confirmed that this reproduction reaches the path where contextual typing through the reverse-mapped property is suppressed by the circularity check and the index signature from the other intersection constituent is subsequently used as the contextual type.
I also tested the current checker with that index-signature fallback temporarily suppressed for this case. With the fallback suppressed, the reproduction reports TS2769, matching the behavior from immediately before #52095.
At this point, I don't know whether the defect is specifically in the index-info fallback.
It may instead be that this reverse-mapped property should not be classified as circular at this stage.
The interesting interaction is that a lookup suppressed by the circularity guard and a lookup where no concrete property is available can both result in no concrete contextual property type being returned, while the subsequent intersection logic does not appear to distinguish why that happened.