Repository navigation
Allow accessing named declared members during a type's resolution - #65059
Anders Hejlsberg (ahejlsberg) wants to merge 6 commits into
Conversation
|
TypeScript Bot (@typescript-bot) test it |
There was a problem hiding this comment.
🟡 Changes recommended
Recursive inherited-property lookups can lose cached symbols after stack reallocation and trigger circular resolution before reaching later base declarations.
2 open issues
What changed in this PR
This PR lets the native TypeScript checker access declared and inherited named members while a type’s member layout is still resolving. It adapts the approach from #64688 to lazy member resolution, addressing the Zod circularity regression in #64605.
Changes:
- Tracks resolving object types and carries early-resolved properties into their completed lazy layouts.
- Adds regression coverage for recursive property access and inherited declared members.
- Updates diagnostic baselines to remove circularity and excessive-instantiation errors.
| File | Description |
|---|---|
tsc/testdata/tests/cases/compiler/circularDeclaredPropertyAccess.ts |
Adds Zod and inherited-member regression cases. |
tsc/testdata/baselines/reference/compiler/sharedMemberLayoutThisIntersectionBase.errors.txt |
Removes excessive-instantiation diagnostics. |
tsc/testdata/baselines/reference/compiler/mutuallyRecursiveInference.errors.txt |
Removes a circular-instantiation diagnostic. |
tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.types |
Records inferred types for the new cases. |
tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.symbols |
Records symbols for the new cases. |
tsc/internal/checker/checker.go |
Enables property lookup during member resolution and preserves resolved symbols. |
Review effort: Balanced
| resolved = c.instantiateSymbol(symbol, r.mapper) | ||
| } else { | ||
| for _, baseType := range r.baseTypes { | ||
| if resolved = c.getPropertyOfTypeEx(baseType, name, true, false); resolved != nil { |
There was a problem hiding this comment.
This is intended. When D extends M<D>, Y we need to fully resolve M<D> in order to know whether it declares a q or whether q is inherited from Y. If that resolution causes a circularity, that circularity will eventually be caught elsewhere.
| } | ||
| } | ||
| } | ||
| if resolved != nil { |
There was a problem hiding this comment.
Yeah, unlikely, but possible. Will fix.
|
Hey Anders Hejlsberg (@ahejlsberg), the results of running the DT tests are ready. Everything looks the same! |
|
Anders Hejlsberg (@ahejlsberg) Here are the results of running the user tests with tsc comparing Everything looks good! |
|
Anders Hejlsberg (@ahejlsberg) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Anders Hejlsberg (@ahejlsberg) Here are the results of running the top 400 repos with tsc comparing Everything looks good! |
|
Interestingly, though perhaps not surprisingly, my branch that is making the checker order-independent / idempotent does actually also fix this problem, it seems. I'll have to finally open that PR and expose everyone to the horrorshow. |
I'm curious to see how. |
|
Basically, during resolution, it installs a temporarily layout of just the declared members. It doesn't search the base types during resolution, though.
I'm currently rebasing it and will send it just so people can play with it. |
|
I sent #65064 just to at least have something out there to point at. |
|
I was wrong before; I think my branch's behavior matches. But I think that this PR is technically order dependent? Hard to come up with an example though, but I can see |
Matches what? |
|
This PR's, since I had already "fixed" this unknowingly. |
|
Hmm, yeah, #65064 has some pretty complex and scary looking code guarded by a new |
Possibly. But I just added a change that excludes class/interface types from |
|
TypeScript Bot (@typescript-bot) test it |
|
Hey Anders Hejlsberg (@ahejlsberg), the results of running the DT tests are ready. Everything looks the same! |
|
Anders Hejlsberg (@ahejlsberg) Here are the results of running the user tests with tsc comparing Everything looks good! |
|
Anders Hejlsberg (@ahejlsberg) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Anders Hejlsberg (@ahejlsberg) Here are the results of running the top 400 repos with tsc comparing Everything looks good! |

This is similar to #64688, but works with the lazy member resolution introduced in #64915 and also ensures that inherited declared members can be accessed during a type's resolution. Closes #64688.
Fixes #64605.