Fix template literal type exponential blowup in addSpans#3175
Open
Fix template literal type exponential blowup in addSpans#3175
Conversation
Add length checks in getTemplateLiteralType's addSpans closure to prevent exponentially growing template literal types from consuming all memory. Two checks are added: 1. sb.Len() > 100_000: caps concrete string literal growth 2. len(newTypes) > 1_000: caps generic placeholder growth When either limit is exceeded, addSpans returns false, causing getTemplateLiteralType to safely return stringType instead of building an infinitely growing type. Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/513b1414-7f99-4616-b0a7-57a74d4b4eb6
Copilot created this pull request from a session on behalf of
RyanCavanaugh
March 20, 2026 15:41
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Adds guardrails to prevent exponential memory blowups when instantiating recursive template literal types, and introduces a regression test reproducing the TypeScript issue.
Changes:
- Add limits in
getTemplateLiteralType/addSpansto cap concrete string growth and placeholder/type expansion. - Add a compiler test case reproducing exponential blowup in recursive template literal types.
- Add baseline
.types,.symbols, and.errors.txtoutputs for the new test.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| internal/checker/checker.go | Adds early-exit guards in addSpans to prevent unbounded growth and fall back to stringType. |
| testdata/tests/cases/compiler/templateLiteralExponentialBlowup.ts | New regression test reproducing recursive template literal exponential growth. |
| testdata/baselines/reference/compiler/templateLiteralExponentialBlowup.types | Baseline for type output of the new regression test. |
| testdata/baselines/reference/compiler/templateLiteralExponentialBlowup.symbols | Baseline for symbol output of the new regression test. |
| testdata/baselines/reference/compiler/templateLiteralExponentialBlowup.errors.txt | Baseline confirming expected TS2589 error for the new regression test. |
Comments suppressed due to low confidence (2)
internal/checker/checker.go:1
- The guard is checked only once per loop iteration, before any
sb.WriteString(...)ornewTypesappends happen. A single iteration can still append a very large literal chunk or expandnewTypesby many entries (e.g., when flattening nested template literal types), potentially blowing past the intended bounds before the next iteration checks the limits. Consider enforcing the limits at the exact growth sites: (1) before/after eachsb.WriteString(preferably pre-checkingsb.Len()+len(toAdd)), and (2) after each append/extend tonewTypes(or via a small helper likeappendNewType(...) boolthat checks the cap immediately).
internal/checker/checker.go:1 - The newly introduced numeric limits are magic numbers. To make the behavior easier to tune and to document the rationale, define named constants (ideally near other instantiation/recursion limits) such as
maxTemplateLiteralConcreteLengthandmaxTemplateLiteralPlaceholderCount, and add a short comment explaining why these specific thresholds were chosen and how they relate to existing depth/tail-recursion limits.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Recursive template literal types like
`${S}_${S}`can double string length each iteration, exhausting memory before the instantiation depth limit (100) or tail recursion limit (1000) is reached.Repro from microsoft/TypeScript#63271:
Two growth vectors exist in
addSpans:stringplaceholdersAdded two guards in the
addSpansloop ingetTemplateLiteralType:sb.Len() > 100_000— bounds concrete string growthlen(newTypes) > 1_000— bounds placeholder count growthEither triggers
addSpansreturningfalse, which falls back tostringType. The existing tail recursion limit then produces the expected TS2589 error.