Skip to content

Commit 430ac1c

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 74a228d commit 430ac1c

File tree

3 files changed

+91
-14
lines changed

3 files changed

+91
-14
lines changed

internal/checker/checker.go

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

2520225202
func (c *Checker) removeStringLiteralsMatchedByTemplateLiterals(types []*Type) []*Type {
2520325203
templates := core.Filter(types, c.isPatternLiteralType)
25204-
if len(templates) != 0 {
25205-
i := len(types)
25206-
for i > 0 {
25207-
i--
25208-
t := types[i]
25209-
if t.flags&TypeFlagsStringLiteral != 0 && core.Some(templates, func(template *Type) bool {
25210-
return c.isTypeMatchedByTemplateLiteralOrStringMapping(t, template)
25211-
}) {
25212-
types = slices.Delete(types, i, i+1)
25213-
}
25204+
if len(templates) == 0 {
25205+
return types
25206+
}
25207+
templateLiterals := core.Filter(templates, func(t *Type) bool {
25208+
return t.flags&TypeFlagsTemplateLiteral != 0
25209+
})
25210+
stringMappings := core.Filter(templates, func(t *Type) bool {
25211+
return t.flags&TypeFlagsStringMapping != 0
25212+
})
25213+
var trie *templateLiteralTrieNode
25214+
if len(templateLiterals) >= 2 {
25215+
trie = c.buildTemplateLiteralTrieFromTypes(templateLiterals)
25216+
}
25217+
i := len(types)
25218+
for i > 0 {
25219+
i--
25220+
t := types[i]
25221+
if t.flags&TypeFlagsStringLiteral != 0 && c.isStringLiteralMatchedByTemplates(t, trie, templateLiterals, stringMappings) {
25222+
types = slices.Delete(types, i, i+1)
2521425223
}
2521525224
}
2521625225
return types
2521725226
}
2521825227

25219-
func (c *Checker) isTypeMatchedByTemplateLiteralOrStringMapping(t *Type, template *Type) bool {
25220-
if template.flags&TypeFlagsTemplateLiteral != 0 {
25221-
return c.isTypeMatchedByTemplateLiteralType(t, template.AsTemplateLiteralType(), c.compareTypesAssignable)
25228+
func (c *Checker) isStringLiteralMatchedByTemplates(source *Type, trie *templateLiteralTrieNode, templateLiterals []*Type, stringMappings []*Type) bool {
25229+
if trie != nil {
25230+
if c.findMatchingTemplateLiteralInTrie(trie, source, c.compareTypesAssignable) != nil {
25231+
return true
25232+
}
25233+
} else if len(templateLiterals) > 0 {
25234+
if core.Some(templateLiterals, func(tl *Type) bool {
25235+
return c.isTypeMatchedByTemplateLiteralType(source, tl.AsTemplateLiteralType(), c.compareTypesAssignable)
25236+
}) {
25237+
return true
25238+
}
2522225239
}
25223-
return c.isMemberOfStringMapping(t, template)
25240+
if len(stringMappings) > 0 {
25241+
if core.Some(stringMappings, func(sm *Type) bool {
25242+
return c.isMemberOfStringMapping(source, sm)
25243+
}) {
25244+
return true
25245+
}
25246+
}
25247+
return false
2522425248
}
2522525249

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

internal/checker/relater.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,54 @@ 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 i := 0; i < len(prefix); i++ {
1114+
ch := prefix[i]
1115+
if node.children == nil {
1116+
node.children = make(map[byte]*templateLiteralTrieNode)
1117+
}
1118+
child := node.children[ch]
1119+
if child == nil {
1120+
child = &templateLiteralTrieNode{}
1121+
node.children[ch] = child
1122+
}
1123+
node = child
1124+
}
1125+
node.types = append(node.types, t)
1126+
}
1127+
return root
1128+
}
1129+
1130+
func (c *Checker) findMatchingTemplateLiteralInTrie(trie *templateLiteralTrieNode, source *Type, compareTypes TypeComparer) *Type {
1131+
value := source.AsLiteralType().Value().(string)
1132+
node := trie
1133+
// Check root candidates (empty-prefix templates like `${string}`)
1134+
for _, t := range node.types {
1135+
if c.isTypeMatchedByTemplateLiteralType(source, t.AsTemplateLiteralType(), compareTypes) {
1136+
return t
1137+
}
1138+
}
1139+
for i := 0; i < len(value); i++ {
1140+
if node.children == nil {
1141+
return nil
1142+
}
1143+
node = node.children[value[i]]
1144+
if node == nil {
1145+
return nil
1146+
}
1147+
for _, t := range node.types {
1148+
if c.isTypeMatchedByTemplateLiteralType(source, t.AsTemplateLiteralType(), compareTypes) {
1149+
return t
1150+
}
1151+
}
1152+
}
1153+
return nil
1154+
}
1155+
11081156
// Return the name of a discriminant property for which it was possible and feasible to construct a map of
11091157
// constituent types keyed by the literal types of the property by that name in each constituent type. Return
11101158
// 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
@@ -1060,6 +1060,11 @@ type TemplateLiteralType struct {
10601060
func (t *TemplateLiteralType) Texts() []string { return t.texts }
10611061
func (t *TemplateLiteralType) Types() []*Type { return t.types }
10621062

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

0 commit comments

Comments
 (0)