Use trie for removeStringLiteralsMatchedByTemplateLiterals#63343
Use trie for removeStringLiteralsMatchedByTemplateLiterals#63343eps1lon wants to merge 1 commit intomicrosoft:mainfrom
Conversation
Optimize removeStringLiteralsMatchedByTemplateLiterals by building a prefix trie from TemplateLiteralType patterns and using O(L) trie traversal per string literal instead of O(m) linear scan across all templates. StringMappingType templates (which cannot be trie-indexed) are checked separately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Is this different than #59759? |
|
Looks like the same at a glance. Yours is already doing some size estimation it seems. I haven't accounted for opting out of trie-based search for small types. |
There was a problem hiding this comment.
Pull request overview
This PR optimizes union reduction in the type checker by accelerating removeStringLiteralsMatchedByTemplateLiterals, reducing repeated template-literal matching work when many template literal types are present.
Changes:
- Adds an internal
TemplateLiteralTrieNodetype to represent a prefix trie for template literal prefixes. - Builds a prefix trie from
TemplateLiteralType.texts[0]and uses it to narrow candidate template literals per string literal. - Separates
StringMappingTypechecks from trie-based template literal checks.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/compiler/types.ts | Adds an internal trie node interface for template-literal prefix indexing. |
| src/compiler/checker.ts | Implements trie construction and lookup, wiring it into removeStringLiteralsMatchedByTemplateLiterals. |
| const templateLiterals = filter(templates, t => !!(t.flags & TypeFlags.TemplateLiteral)) as TemplateLiteralType[]; | ||
| const stringMappings = filter(templates, t => !!(t.flags & TypeFlags.StringMapping)) as StringMappingType[]; | ||
| const trie = templateLiterals.length >= 2 ? buildTemplateLiteralTrieFromTypes(templateLiterals) : undefined; |
There was a problem hiding this comment.
trie is built whenever there are ≥2 TemplateLiteralTypes, but if most/all templates have an empty texts[0] (e.g. patterns starting with a placeholder like ${string}...), they all end up in root.types and the trie provides little/no pruning while still paying the build + allocation cost. Consider gating trie construction on having enough non-empty prefixes (or splitting empty-prefix templates into a separate array checked linearly) so this optimization doesn't regress cases dominated by empty-prefix templates.
Started out as a guess that template literal checking should be done using a trie which led to
removeStringLiteralsMatchedByTemplateLiteralsbeing the major contributor in CPU traces.Actual implementation is vibe-coded with Claude Opus 4.6 (1M context)
Tested this in our internal apps (~90s
tscduration before). Ideally we'd run this against TypeScript's extensive benchmarks since this a space vs runtime tradeoff so we might want to opt out of building the trie for small unions.Optimize
removeStringLiteralsMatchedByTemplateLiteralsby building a prefix trie fromTemplateLiteralTypepatterns and using O(L) trie traversal per string literal instead of O(m) linear scan across all templates.StringMappingTypetemplates (which cannot be trie-indexed) are checked separately.Fixes #63342