Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -25193,26 +25193,50 @@ func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags,

func (c *Checker) removeStringLiteralsMatchedByTemplateLiterals(types []*Type) []*Type {
templates := core.Filter(types, c.isPatternLiteralType)
if len(templates) != 0 {
i := len(types)
for i > 0 {
i--
t := types[i]
if t.flags&TypeFlagsStringLiteral != 0 && core.Some(templates, func(template *Type) bool {
return c.isTypeMatchedByTemplateLiteralOrStringMapping(t, template)
}) {
types = slices.Delete(types, i, i+1)
}
if len(templates) == 0 {
return types
}
templateLiterals := core.Filter(templates, func(t *Type) bool {
return t.flags&TypeFlagsTemplateLiteral != 0
})
stringMappings := core.Filter(templates, func(t *Type) bool {
return t.flags&TypeFlagsStringMapping != 0
})
var trie *templateLiteralTrieNode
if len(templateLiterals) >= 2 {
trie = c.buildTemplateLiteralTrieFromTypes(templateLiterals)
}
i := len(types)
for i > 0 {
i--
t := types[i]
if t.flags&TypeFlagsStringLiteral != 0 && c.isStringLiteralMatchedByTemplates(t, trie, templateLiterals, stringMappings) {
types = slices.Delete(types, i, i+1)
}
}
return types
}

func (c *Checker) isTypeMatchedByTemplateLiteralOrStringMapping(t *Type, template *Type) bool {
if template.flags&TypeFlagsTemplateLiteral != 0 {
return c.isTypeMatchedByTemplateLiteralType(t, template.AsTemplateLiteralType(), c.compareTypesAssignable)
func (c *Checker) isStringLiteralMatchedByTemplates(source *Type, trie *templateLiteralTrieNode, templateLiterals []*Type, stringMappings []*Type) bool {
if trie != nil {
if c.findMatchingTemplateLiteralInTrie(trie, source, c.compareTypesAssignable) != nil {
return true
}
} else if len(templateLiterals) > 0 {
if core.Some(templateLiterals, func(tl *Type) bool {
return c.isTypeMatchedByTemplateLiteralType(source, tl.AsTemplateLiteralType(), c.compareTypesAssignable)
}) {
return true
}
}
return c.isMemberOfStringMapping(t, template)
if len(stringMappings) > 0 {
if core.Some(stringMappings, func(sm *Type) bool {
return c.isMemberOfStringMapping(source, sm)
}) {
return true
}
}
return false
}

func (c *Checker) removeConstrainedTypeVariables(types []*Type) []*Type {
Expand Down
47 changes: 47 additions & 0 deletions internal/checker/relater.go
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,53 @@ func (c *Checker) getMatchingUnionConstituentForType(unionType *Type, t *Type) *
return c.getConstituentTypeForKeyType(unionType, propType)
}

func (c *Checker) buildTemplateLiteralTrieFromTypes(templateTypes []*Type) *templateLiteralTrieNode {
root := &templateLiteralTrieNode{}
for _, t := range templateTypes {
prefix := t.AsTemplateLiteralType().texts[0]
node := root
for _, ch := range []byte(prefix) {
if node.children == nil {
node.children = make(map[byte]*templateLiteralTrieNode)
}
child := node.children[ch]
if child == nil {
child = &templateLiteralTrieNode{}
node.children[ch] = child
}
node = child
}
node.types = append(node.types, t)
}
return root
}

func (c *Checker) findMatchingTemplateLiteralInTrie(trie *templateLiteralTrieNode, source *Type, compareTypes TypeComparer) *Type {
value := source.AsLiteralType().Value().(string)
node := trie
// Check root candidates (empty-prefix templates like `${string}`)
for _, t := range node.types {
if c.isTypeMatchedByTemplateLiteralType(source, t.AsTemplateLiteralType(), compareTypes) {
return t
}
}
for _, ch := range []byte(value) {
if node.children == nil {
return nil
}
node = node.children[ch]
if node == nil {
return nil
}
for _, t := range node.types {
if c.isTypeMatchedByTemplateLiteralType(source, t.AsTemplateLiteralType(), compareTypes) {
return t
}
}
}
return nil
}

// Return the name of a discriminant property for which it was possible and feasible to construct a map of
// constituent types keyed by the literal types of the property by that name in each constituent type. Return
// an empty string if no such discriminant property exists.
Expand Down
5 changes: 5 additions & 0 deletions internal/checker/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,11 @@ type TemplateLiteralType struct {
func (t *TemplateLiteralType) Texts() []string { return t.texts }
func (t *TemplateLiteralType) Types() []*Type { return t.types }

type templateLiteralTrieNode struct {
children map[byte]*templateLiteralTrieNode
types []*Type // template literal types whose prefix ends at this node
}

type StringMappingType struct {
ConstrainedType
target *Type
Expand Down
Loading