Skip to content

Commit d7118cd

Browse files
eps1lonclaude
andcommitted
Add trie-based fast path for template literal union assignability
When checking if a string literal is assignable to a union containing 5+ template literal types, use a lazily-built prefix trie cached on the UnionType to find matching templates in O(L) instead of O(n) linear scan. This avoids calling isTypeMatchedByTemplateLiteralType against every union member. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b318e23 commit d7118cd

File tree

2 files changed

+60
-5
lines changed

2 files changed

+60
-5
lines changed

internal/checker/relater.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,29 @@ func (c *Checker) findMatchingTemplateLiteralInTrie(trie *templateLiteralTrieNod
11521152
return nil
11531153
}
11541154

1155+
func (c *Checker) buildTemplateLiteralTrie(unionType *Type) *templateLiteralTrieNode {
1156+
types := unionType.Types()
1157+
var templateTypes []*Type
1158+
for _, t := range types {
1159+
if t.flags&TypeFlagsTemplateLiteral != 0 {
1160+
templateTypes = append(templateTypes, t)
1161+
}
1162+
}
1163+
if len(templateTypes) < 5 {
1164+
return nil
1165+
}
1166+
return c.buildTemplateLiteralTrieFromTypes(templateTypes)
1167+
}
1168+
1169+
func (c *Checker) getTemplateLiteralTrie(unionType *Type) *templateLiteralTrieNode {
1170+
u := unionType.AsUnionType()
1171+
if !u.templateLiteralTrieComputed {
1172+
u.templateLiteralTrie = c.buildTemplateLiteralTrie(unionType)
1173+
u.templateLiteralTrieComputed = true
1174+
}
1175+
return u.templateLiteralTrie
1176+
}
1177+
11551178
// Return the name of a discriminant property for which it was possible and feasible to construct a map of
11561179
// constituent types keyed by the literal types of the property by that name in each constituent type. Return
11571180
// an empty string if no such discriminant property exists.
@@ -3013,6 +3036,36 @@ func (r *Relater) typeRelatedToSomeType(source *Type, target *Type, reportErrors
30133036
}
30143037
}
30153038
}
3039+
// Fast path: trie-based lookup for string literal source against unions with template literals
3040+
if source.flags&TypeFlagsStringLiteral != 0 {
3041+
trie := r.c.getTemplateLiteralTrie(target)
3042+
if trie != nil {
3043+
trieMatch := r.c.findMatchingTemplateLiteralInTrie(trie, source, r.isRelatedToWorker)
3044+
if trieMatch != nil {
3045+
related := r.isRelatedToEx(source, trieMatch, RecursionFlagsTarget, false /*reportErrors*/, nil /*headMessage*/, intersectionState)
3046+
if related != TernaryFalse {
3047+
return related
3048+
}
3049+
}
3050+
// Trie covered all template literals; containsType covered all string literals.
3051+
// Check remaining non-literal, non-template members.
3052+
for _, t := range targetTypes {
3053+
if t.flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral) == 0 {
3054+
related := r.isRelatedToEx(source, t, RecursionFlagsTarget, false /*reportErrors*/, nil /*headMessage*/, intersectionState)
3055+
if related != TernaryFalse {
3056+
return related
3057+
}
3058+
}
3059+
}
3060+
if reportErrors {
3061+
bestMatchingType := r.c.getBestMatchingType(source, target, r.isRelatedToSimple)
3062+
if bestMatchingType != nil {
3063+
r.isRelatedToEx(source, bestMatchingType, RecursionFlagsTarget, true /*reportErrors*/, nil /*headMessage*/, intersectionState)
3064+
}
3065+
}
3066+
return TernaryFalse
3067+
}
3068+
}
30163069
for _, t := range targetTypes {
30173070
related := r.isRelatedToEx(source, t, RecursionFlagsTarget, false /*reportErrors*/, nil /*headMessage*/, intersectionState)
30183071
if related != TernaryFalse {

internal/checker/types.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -989,11 +989,13 @@ func (t *UnionOrIntersectionType) Types() []*Type {
989989

990990
type UnionType struct {
991991
UnionOrIntersectionType
992-
resolvedReducedType *Type
993-
regularType *Type
994-
origin *Type // Denormalized union, intersection, or index type in which union originates
995-
keyPropertyName string // Property with unique unit type that exists in every object/intersection in union type
996-
constituentMap map[*Type]*Type // Constituents keyed by unit type discriminants
992+
resolvedReducedType *Type
993+
regularType *Type
994+
origin *Type // Denormalized union, intersection, or index type in which union originates
995+
keyPropertyName string // Property with unique unit type that exists in every object/intersection in union type
996+
constituentMap map[*Type]*Type // Constituents keyed by unit type discriminants
997+
templateLiteralTrie *templateLiteralTrieNode
998+
templateLiteralTrieComputed bool
997999
}
9981000

9991001
// IntersectionType

0 commit comments

Comments
 (0)