@@ -1058,6 +1058,7 @@ import {
10581058 SyntheticExpression,
10591059 TaggedTemplateExpression,
10601060 TemplateExpression,
1061+ TemplateLiteralTrieNode,
10611062 TemplateLiteralType,
10621063 TemplateLiteralTypeNode,
10631064 Ternary,
@@ -18219,21 +18220,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1821918220 function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) {
1822018221 const templates = filter(types, isPatternLiteralType) as (TemplateLiteralType | StringMappingType)[];
1822118222 if (templates.length) {
18223+ const templateLiterals = filter(templates, t => !!(t.flags & TypeFlags.TemplateLiteral)) as TemplateLiteralType[];
18224+ const stringMappings = filter(templates, t => !!(t.flags & TypeFlags.StringMapping)) as StringMappingType[];
18225+ const trie = templateLiterals.length >= 2 ? buildTemplateLiteralTrieFromTypes(templateLiterals) : undefined;
1822218226 let i = types.length;
1822318227 while (i > 0) {
1822418228 i--;
1822518229 const t = types[i];
18226- if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeMatchedByTemplateLiteralOrStringMapping(t, template) )) {
18230+ if (t.flags & TypeFlags.StringLiteral && isStringLiteralMatchedByTemplates(t as StringLiteralType, trie, templateLiterals, stringMappings )) {
1822718231 orderedRemoveItemAt(types, i);
1822818232 }
1822918233 }
1823018234 }
1823118235 }
1823218236
18233- function isTypeMatchedByTemplateLiteralOrStringMapping(type: Type, template: TemplateLiteralType | StringMappingType) {
18234- return template.flags & TypeFlags.TemplateLiteral ?
18235- isTypeMatchedByTemplateLiteralType(type, template as TemplateLiteralType) :
18236- isMemberOfStringMapping(type, template);
18237+ function isStringLiteralMatchedByTemplates(source: StringLiteralType, trie: TemplateLiteralTrieNode | undefined, templateLiterals: readonly TemplateLiteralType[], stringMappings: readonly StringMappingType[]): boolean {
18238+ if (trie) {
18239+ if (findMatchingTemplateLiteralInTrie(trie, source)) return true;
18240+ }
18241+ else if (templateLiterals.length) {
18242+ if (some(templateLiterals, tl => isTypeMatchedByTemplateLiteralType(source, tl))) return true;
18243+ }
18244+ if (stringMappings.length) {
18245+ if (some(stringMappings, sm => isMemberOfStringMapping(source, sm))) return true;
18246+ }
18247+ return false;
1823718248 }
1823818249
1823918250 function removeConstrainedTypeVariables(types: Type[]) {
@@ -28062,6 +28073,58 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2806228073 return propType && getConstituentTypeForKeyType(unionType, propType);
2806328074 }
2806428075
28076+ function buildTemplateLiteralTrieFromTypes(templateTypes: readonly TemplateLiteralType[]): TemplateLiteralTrieNode {
28077+ const root: TemplateLiteralTrieNode = {};
28078+ for (const templateType of templateTypes) {
28079+ const prefix = templateType.texts[0];
28080+ let node = root;
28081+ for (let i = 0; i < prefix.length; i++) {
28082+ const ch = prefix.charCodeAt(i);
28083+ if (!node.children) {
28084+ node.children = new Map();
28085+ }
28086+ let child = node.children.get(ch);
28087+ if (!child) {
28088+ child = {};
28089+ node.children.set(ch, child);
28090+ }
28091+ node = child;
28092+ }
28093+ if (!node.types) {
28094+ node.types = [];
28095+ }
28096+ node.types.push(templateType);
28097+ }
28098+ return root;
28099+ }
28100+
28101+ function findMatchingTemplateLiteralInTrie(trie: TemplateLiteralTrieNode, source: StringLiteralType): TemplateLiteralType | undefined {
28102+ const value = source.value;
28103+ let node: TemplateLiteralTrieNode | undefined = trie;
28104+ // Check root candidates (empty-prefix templates like `${string}`)
28105+ if (node.types) {
28106+ for (const type of node.types) {
28107+ if (isTypeMatchedByTemplateLiteralType(source, type)) {
28108+ return type;
28109+ }
28110+ }
28111+ }
28112+ for (let i = 0; i < value.length; i++) {
28113+ node = node.children?.get(value.charCodeAt(i));
28114+ if (!node) {
28115+ return undefined;
28116+ }
28117+ if (node.types) {
28118+ for (const type of node.types) {
28119+ if (isTypeMatchedByTemplateLiteralType(source, type)) {
28120+ return type;
28121+ }
28122+ }
28123+ }
28124+ }
28125+ return undefined;
28126+ }
28127+
2806528128 function getMatchingUnionConstituentForObjectLiteral(unionType: UnionType, node: ObjectLiteralExpression) {
2806628129 const keyPropertyName = getKeyPropertyName(unionType);
2806728130 const propNode = keyPropertyName && find(node.properties, p =>
0 commit comments