Skip to content

Commit b318e23

Browse files
eps1lonclaude
andcommitted
Use trie for removeStringLiteralsMatchedByTemplateLiterals
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>
1 parent 2995a71 commit b318e23

File tree

3 files changed

+90
-14
lines changed

3 files changed

+90
-14
lines changed

internal/checker/checker.go

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25193,26 +25193,50 @@ func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags,
2519325193

2519425194
func (c *Checker) removeStringLiteralsMatchedByTemplateLiterals(types []*Type) []*Type {
2519525195
templates := core.Filter(types, c.isPatternLiteralType)
25196-
if len(templates) != 0 {
25197-
i := len(types)
25198-
for i > 0 {
25199-
i--
25200-
t := types[i]
25201-
if t.flags&TypeFlagsStringLiteral != 0 && core.Some(templates, func(template *Type) bool {
25202-
return c.isTypeMatchedByTemplateLiteralOrStringMapping(t, template)
25203-
}) {
25204-
types = slices.Delete(types, i, i+1)
25205-
}
25196+
if len(templates) == 0 {
25197+
return types
25198+
}
25199+
templateLiterals := core.Filter(templates, func(t *Type) bool {
25200+
return t.flags&TypeFlagsTemplateLiteral != 0
25201+
})
25202+
stringMappings := core.Filter(templates, func(t *Type) bool {
25203+
return t.flags&TypeFlagsStringMapping != 0
25204+
})
25205+
var trie *templateLiteralTrieNode
25206+
if len(templateLiterals) >= 2 {
25207+
trie = c.buildTemplateLiteralTrieFromTypes(templateLiterals)
25208+
}
25209+
i := len(types)
25210+
for i > 0 {
25211+
i--
25212+
t := types[i]
25213+
if t.flags&TypeFlagsStringLiteral != 0 && c.isStringLiteralMatchedByTemplates(t, trie, templateLiterals, stringMappings) {
25214+
types = slices.Delete(types, i, i+1)
2520625215
}
2520725216
}
2520825217
return types
2520925218
}
2521025219

25211-
func (c *Checker) isTypeMatchedByTemplateLiteralOrStringMapping(t *Type, template *Type) bool {
25212-
if template.flags&TypeFlagsTemplateLiteral != 0 {
25213-
return c.isTypeMatchedByTemplateLiteralType(t, template.AsTemplateLiteralType(), c.compareTypesAssignable)
25220+
func (c *Checker) isStringLiteralMatchedByTemplates(source *Type, trie *templateLiteralTrieNode, templateLiterals []*Type, stringMappings []*Type) bool {
25221+
if trie != nil {
25222+
if c.findMatchingTemplateLiteralInTrie(trie, source, c.compareTypesAssignable) != nil {
25223+
return true
25224+
}
25225+
} else if len(templateLiterals) > 0 {
25226+
if core.Some(templateLiterals, func(tl *Type) bool {
25227+
return c.isTypeMatchedByTemplateLiteralType(source, tl.AsTemplateLiteralType(), c.compareTypesAssignable)
25228+
}) {
25229+
return true
25230+
}
2521425231
}
25215-
return c.isMemberOfStringMapping(t, template)
25232+
if len(stringMappings) > 0 {
25233+
if core.Some(stringMappings, func(sm *Type) bool {
25234+
return c.isMemberOfStringMapping(source, sm)
25235+
}) {
25236+
return true
25237+
}
25238+
}
25239+
return false
2521625240
}
2521725241

2521825242
func (c *Checker) removeConstrainedTypeVariables(types []*Type) []*Type {

internal/checker/relater.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,53 @@ func (c *Checker) getMatchingUnionConstituentForType(unionType *Type, t *Type) *
11051105
return c.getConstituentTypeForKeyType(unionType, propType)
11061106
}
11071107

1108+
func (c *Checker) buildTemplateLiteralTrieFromTypes(templateTypes []*Type) *templateLiteralTrieNode {
1109+
root := &templateLiteralTrieNode{}
1110+
for _, t := range templateTypes {
1111+
prefix := t.AsTemplateLiteralType().texts[0]
1112+
node := root
1113+
for _, ch := range []byte(prefix) {
1114+
if node.children == nil {
1115+
node.children = make(map[byte]*templateLiteralTrieNode)
1116+
}
1117+
child := node.children[ch]
1118+
if child == nil {
1119+
child = &templateLiteralTrieNode{}
1120+
node.children[ch] = child
1121+
}
1122+
node = child
1123+
}
1124+
node.types = append(node.types, t)
1125+
}
1126+
return root
1127+
}
1128+
1129+
func (c *Checker) findMatchingTemplateLiteralInTrie(trie *templateLiteralTrieNode, source *Type, compareTypes TypeComparer) *Type {
1130+
value := source.AsLiteralType().Value().(string)
1131+
node := trie
1132+
// Check root candidates (empty-prefix templates like `${string}`)
1133+
for _, t := range node.types {
1134+
if c.isTypeMatchedByTemplateLiteralType(source, t.AsTemplateLiteralType(), compareTypes) {
1135+
return t
1136+
}
1137+
}
1138+
for _, ch := range []byte(value) {
1139+
if node.children == nil {
1140+
return nil
1141+
}
1142+
node = node.children[ch]
1143+
if node == nil {
1144+
return nil
1145+
}
1146+
for _, t := range node.types {
1147+
if c.isTypeMatchedByTemplateLiteralType(source, t.AsTemplateLiteralType(), compareTypes) {
1148+
return t
1149+
}
1150+
}
1151+
}
1152+
return nil
1153+
}
1154+
11081155
// Return the name of a discriminant property for which it was possible and feasible to construct a map of
11091156
// constituent types keyed by the literal types of the property by that name in each constituent type. Return
11101157
// an empty string if no such discriminant property exists.

internal/checker/types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,11 @@ type TemplateLiteralType struct {
10591059
func (t *TemplateLiteralType) Texts() []string { return t.texts }
10601060
func (t *TemplateLiteralType) Types() []*Type { return t.types }
10611061

1062+
type templateLiteralTrieNode struct {
1063+
children map[byte]*templateLiteralTrieNode
1064+
types []*Type // template literal types whose prefix ends at this node
1065+
}
1066+
10621067
type StringMappingType struct {
10631068
ConstrainedType
10641069
target *Type

0 commit comments

Comments
 (0)