Skip to content

fix(relater): add property comparison cache fast path to avoid false TS2321#4382

Open
maoger wants to merge 2 commits into
microsoft:mainfrom
maoger:fix/ts2321-excessive-stack-depth
Open

fix(relater): add property comparison cache fast path to avoid false TS2321#4382
maoger wants to merge 2 commits into
microsoft:mainfrom
maoger:fix/ts2321-excessive-stack-depth

Conversation

@maoger

@maoger maoger commented Jun 20, 2026

Copy link
Copy Markdown

Summary

Fix false TS2321 ("Excessive stack depth comparing types") errors when comparing large object literals (100+ properties) against complex target types, by adding a relation-cache fast path in isPropertySymbolTypeRelated.

Problem

When comparing large object literals (e.g. defineConfig({...}) with 190+ lint rules) against complex target types, tsgo produces false TS2321 errors that the original tsc does not report.

The root cause: each property comparison in a large object literal goes through the full isRelatedToExrecursiveTypeRelatedTo path, even when the relation cache already has a result for that type pair. When many properties share the same type (e.g. RuleConfig, string), this causes redundant stack pushes that exhaust the recursion limit.

Changes

internal/checker/relater.go

Add a relation-cache lookup fast path in isPropertySymbolTypeRelated, before the isRelatedToEx call:

if effectiveSource.flags&TypeFlagsObject != 0 && effectiveTarget.flags&TypeFlagsObject != 0 {
    id, _ := getRelationKey(effectiveSource, effectiveTarget, intersectionState,
        r.relation == r.c.identityRelation, false /*ignoreConstraints*/)
    if entry := r.relation.get(id); entry != RelationComparisonResultNone &&
        entry&RelationComparisonResultSucceeded != 0 {
        r.c.reliabilityFlags |= entry & (RelationComparisonResultReportsUnmeasurable | RelationComparisonResultReportsUnreliable)
        return TernaryTrue
    }
}
  • Only triggers for object-to-object property comparisons where the cache already holds a success result
  • Propagates cached reliability flags to match recursiveTypeRelatedTo behavior
  • Does not affect the recursion limit or change semantics for non-cached paths

Scope: This fix targets false TS2321 caused by repeated property-type comparisons in large object literals. It does not address cases where recursion depth is inherently deep (e.g. recursive conditional types).

testdata/tests/cases/compiler/largeObjectLiteralNoExcessiveStackDepth.ts (new)

Regression test with two scenarios:

  • 200 RuleConfig-typed properties assigned to a LargeConfig target
  • 150 function-typed (FnType) properties assigned to a ConfigWithFunctions target

Both compile with zero errors (no TS2321).

Testing

go test ./internal/checker/          # all existing tests pass
go test ./internal/testrunner/       # all tests pass, including new regression test

Related Issues

@maoger

maoger commented Jun 20, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@maoger maoger marked this pull request as ready for review June 20, 2026 10:12
Copilot AI review requested due to automatic review settings June 20, 2026 10:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the checker’s type-relating logic to reduce false TS2321 (“Excessive stack depth comparing types”) reports when comparing very large object literals against complex target types.

Changes:

  • Increase the relater recursion stack depth limit from 100 to 400.
  • Add a relation-cache lookup fast path during per-property type comparisons.
  • Add a new compiler regression test (plus baselines) exercising large object-literal assignment scenarios.

Reviewed changes

Copilot reviewed 3 out of 5 changed files in this pull request and generated 3 comments.

File Description
internal/checker/relater.go Raises stack depth limit and adds a property-comparison cache fast path in the relater.
testdata/tests/cases/compiler/largeObjectLiteralNoExcessiveStackDepth.ts New regression test covering large object literal assignments that previously triggered false TS2321.
testdata/baselines/reference/compiler/largeObjectLiteralNoExcessiveStackDepth.types New/updated .types baseline for the regression test.
testdata/baselines/reference/compiler/excessivelyDeepConditionalTypes.errors.txt Baseline update reflecting changed TS2321 printing/trigger point after increasing the stack depth limit.

Comment thread internal/checker/relater.go Outdated
Comment on lines 3103 to 3106
if len(r.sourceStack) == 400 || len(r.targetStack) == 400 {
r.overflow = true
return TernaryFalse
}
Comment thread internal/checker/relater.go Outdated
Comment on lines +4335 to +4338
// Fast path: query the relation cache directly before entering the recursive comparison
// path. This avoids unnecessary stack depth growth when comparing large object literals
// (e.g. Vite/Linter configs with 100+ rules) against complex target types, where each
// property comparison would otherwise push onto the source/target stacks.
Comment on lines +4339 to +4344
if effectiveSource.flags&TypeFlagsObject != 0 && effectiveTarget.flags&TypeFlagsObject != 0 {
id, _ := getRelationKey(effectiveSource, effectiveTarget, intersectionState, r.relation == r.c.identityRelation, false /*ignoreConstraints*/)
if entry := r.relation.get(id); entry != RelationComparisonResultNone && entry&RelationComparisonResultSucceeded != 0 {
return TernaryTrue
}
}
@jakebailey

Copy link
Copy Markdown
Member

We are not going to be raising any limits

#1637 (comment)

@maoger maoger changed the title fix(relater): raise stack depth limit and add property comparison fast path fix(relater): add property comparison cache fast path to avoid false TS2321 Jun 20, 2026
maoger added 2 commits June 20, 2026 17:10
…TS2321

Add a relation-cache lookup fast path in isPropertySymbolTypeRelated that short-circuits on confirmed cached success before entering the full isRelatedToEx recursive comparison. This reduces redundant stack depth growth when comparing large object literals (100+ properties) against complex target types, where many properties share the same type (e.g. RuleConfig, string). The fast path also propagates cached reliability flags (ReportsUnmeasurable/ReportsUnreliable) to match recursiveTypeRelatedTo behavior.
Add largeObjectLiteralNoExcessiveStackDepth.ts with two scenarios: 200 RuleConfig-typed properties and 150 function-typed properties, both assigned to complex target types. Previously these would trigger false TS2321 errors; with the property cache fast path they compile with zero errors.
@maoger maoger force-pushed the fix/ts2321-excessive-stack-depth branch from 34bccf1 to a997839 Compare June 20, 2026 16:10
@maoger

maoger commented Jun 20, 2026

Copy link
Copy Markdown
Author

Thanks for the feedback. I've updated the PR to remove the stack depth limit change — the fix now relies solely on a relation-cache fast path in isPropertySymbolTypeRelated that short-circuits on cached results before entering the recursive comparison path. This avoids redundant stack pushes for repeated property types (e.g. 200 RuleConfig properties) without raising any limits.

The full test suite passes (go test ./internal/checker/... and go test ./internal/testrunner/...), and the excessivelyDeepConditionalTypes.ts baseline is unaffected.

The PR is ready for review when you have time.

@jakebailey

Copy link
Copy Markdown
Member

I'm a little unclear as to the history of this change. Where did it come from? Is this an issue in TS 6? Why is there no issue for this? Have you experienced this in a real codebase?

You're also certainly not disclosing the tooling you're using for this; you're replying like an LLM. https://github.com/microsoft/typescript-go/blob/main/CONTRIBUTING.md#use-of-ai-assistance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants