Skip to content

Commit 40b8122

Browse files
authored
Remove string literals from unions with matching template literals (#41276)
* Remove string literals from unions with matching template literals * Add tests * Accept new baselines
1 parent 71cd5d5 commit 40b8122

File tree

6 files changed

+84
-0
lines changed

6 files changed

+84
-0
lines changed

src/compiler/checker.ts

+17
Original file line numberDiff line numberDiff line change
@@ -13108,6 +13108,20 @@ namespace ts {
1310813108
}
1310913109
}
1311013110

13111+
function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) {
13112+
const templates = filter(types, isPatternLiteralType);
13113+
if (templates.length) {
13114+
let i = types.length;
13115+
while (i > 0) {
13116+
i--;
13117+
const t = types[i];
13118+
if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeSubtypeOf(t, template))) {
13119+
orderedRemoveItemAt(types, i);
13120+
}
13121+
}
13122+
}
13123+
}
13124+
1311113125
// We sort and deduplicate the constituent types based on object identity. If the subtypeReduction
1311213126
// flag is specified we also reduce the constituent type set to only include types that aren't subtypes
1311313127
// of other types. Subtype reduction is expensive for large union types and is possible only when union
@@ -13133,6 +13147,9 @@ namespace ts {
1313313147
if (includes & (TypeFlags.Literal | TypeFlags.UniqueESSymbol)) {
1313413148
removeRedundantLiteralTypes(typeSet, includes);
1313513149
}
13150+
if (includes & TypeFlags.StringLiteral && includes & TypeFlags.TemplateLiteral) {
13151+
removeStringLiteralsMatchedByTemplateLiterals(typeSet);
13152+
}
1313613153
break;
1313713154
case UnionReduction.Subtype:
1313813155
if (!removeSubtypes(typeSet, !(includes & TypeFlags.IncludesStructuredOrInstantiable))) {

tests/baselines/reference/templateLiteralTypesPatterns.errors.txt

+9
Original file line numberDiff line numberDiff line change
@@ -338,4 +338,13 @@ tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(160,7): er
338338

339339
var aa: '0';
340340
var aa: '0' & `${number}`;
341+
342+
// Remove string literals from unions with matching template literals
343+
344+
let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo'
345+
let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo'
346+
let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo`
347+
348+
var bb: `${number}`;
349+
var bb: `${number}` | '0';
341350

tests/baselines/reference/templateLiteralTypesPatterns.js

+15
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ const exampleGood: B = "1 2"; // ok
165165

166166
var aa: '0';
167167
var aa: '0' & `${number}`;
168+
169+
// Remove string literals from unions with matching template literals
170+
171+
let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo'
172+
let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo'
173+
let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo`
174+
175+
var bb: `${number}`;
176+
var bb: `${number}` | '0';
168177
169178
170179
//// [templateLiteralTypesPatterns.js]
@@ -289,3 +298,9 @@ var exampleGood = "1 2"; // ok
289298
// Repro from #41161
290299
var aa;
291300
var aa;
301+
// Remove string literals from unions with matching template literals
302+
var t1; // `foo${string}` | '1foo'
303+
var t2; // `foo${string}` | '1foo' | 'xfoo'
304+
var t3; // `foo${string}` | xfoo' | `${number}foo`
305+
var bb;
306+
var bb;

tests/baselines/reference/templateLiteralTypesPatterns.symbols

+17
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,20 @@ var aa: '0';
401401
var aa: '0' & `${number}`;
402402
>aa : Symbol(aa, Decl(templateLiteralTypesPatterns.ts, 164, 3), Decl(templateLiteralTypesPatterns.ts, 165, 3))
403403

404+
// Remove string literals from unions with matching template literals
405+
406+
let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo'
407+
>t1 : Symbol(t1, Decl(templateLiteralTypesPatterns.ts, 169, 3))
408+
409+
let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo'
410+
>t2 : Symbol(t2, Decl(templateLiteralTypesPatterns.ts, 170, 3))
411+
412+
let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo`
413+
>t3 : Symbol(t3, Decl(templateLiteralTypesPatterns.ts, 171, 3))
414+
415+
var bb: `${number}`;
416+
>bb : Symbol(bb, Decl(templateLiteralTypesPatterns.ts, 173, 3), Decl(templateLiteralTypesPatterns.ts, 174, 3))
417+
418+
var bb: `${number}` | '0';
419+
>bb : Symbol(bb, Decl(templateLiteralTypesPatterns.ts, 173, 3), Decl(templateLiteralTypesPatterns.ts, 174, 3))
420+

tests/baselines/reference/templateLiteralTypesPatterns.types

+17
Original file line numberDiff line numberDiff line change
@@ -560,3 +560,20 @@ var aa: '0';
560560
var aa: '0' & `${number}`;
561561
>aa : "0"
562562

563+
// Remove string literals from unions with matching template literals
564+
565+
let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo'
566+
>t1 : `foo${string}` | "1foo"
567+
568+
let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo'
569+
>t2 : `foo${string}` | "1foo" | "xfoo"
570+
571+
let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo`
572+
>t3 : `foo${string}` | "xfoo" | `${number}foo`
573+
574+
var bb: `${number}`;
575+
>bb : `${number}`
576+
577+
var bb: `${number}` | '0';
578+
>bb : `${number}`
579+

tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts

+9
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,12 @@ const exampleGood: B = "1 2"; // ok
165165

166166
var aa: '0';
167167
var aa: '0' & `${number}`;
168+
169+
// Remove string literals from unions with matching template literals
170+
171+
let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo'
172+
let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo'
173+
let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo`
174+
175+
var bb: `${number}`;
176+
var bb: `${number}` | '0';

0 commit comments

Comments
 (0)