From 3f8f5ca08374f05e9b8d12e520ff0a1550c10eb4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 27 Oct 2020 10:32:07 -0700 Subject: [PATCH 1/3] Remove string literals from unions with matching template literals --- src/compiler/checker.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 19a7085f7d069..6a9e7d1013f84 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13100,6 +13100,20 @@ namespace ts { } } + function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) { + const templates = filter(types, isPatternLiteralType); + if (templates.length) { + let i = types.length; + while (i > 0) { + i--; + const t = types[i]; + if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeSubtypeOf(t, template))) { + orderedRemoveItemAt(types, i); + } + } + } + } + // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction // flag is specified we also reduce the constituent type set to only include types that aren't subtypes // of other types. Subtype reduction is expensive for large union types and is possible only when union @@ -13125,6 +13139,9 @@ namespace ts { if (includes & (TypeFlags.Literal | TypeFlags.UniqueESSymbol)) { removeRedundantLiteralTypes(typeSet, includes); } + if (includes & TypeFlags.StringLiteral && includes & TypeFlags.TemplateLiteral) { + removeStringLiteralsMatchedByTemplateLiterals(typeSet); + } break; case UnionReduction.Subtype: if (!removeSubtypes(typeSet, !(includes & TypeFlags.IncludesStructuredOrInstantiable))) { From d16b629483fc4e621ffc81e1d4ee0ae41cafcdaa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 27 Oct 2020 10:40:08 -0700 Subject: [PATCH 2/3] Add tests --- .../types/literal/templateLiteralTypesPatterns.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts b/tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts index 494fff1768039..b8f5fad334bb7 100644 --- a/tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts +++ b/tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts @@ -165,3 +165,12 @@ const exampleGood: B = "1 2"; // ok var aa: '0'; var aa: '0' & `${number}`; + +// Remove string literals from unions with matching template literals + +let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo' +let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo' +let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo` + +var bb: `${number}`; +var bb: `${number}` | '0'; From 5953bb322112d8b30828add4bf51e4e1d533cd06 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 27 Oct 2020 10:40:15 -0700 Subject: [PATCH 3/3] Accept new baselines --- .../templateLiteralTypesPatterns.errors.txt | 9 +++++++++ .../reference/templateLiteralTypesPatterns.js | 15 +++++++++++++++ .../templateLiteralTypesPatterns.symbols | 17 +++++++++++++++++ .../templateLiteralTypesPatterns.types | 17 +++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/tests/baselines/reference/templateLiteralTypesPatterns.errors.txt b/tests/baselines/reference/templateLiteralTypesPatterns.errors.txt index 652eee291b6e4..672e2d9b6256b 100644 --- a/tests/baselines/reference/templateLiteralTypesPatterns.errors.txt +++ b/tests/baselines/reference/templateLiteralTypesPatterns.errors.txt @@ -338,4 +338,13 @@ tests/cases/conformance/types/literal/templateLiteralTypesPatterns.ts(160,7): er var aa: '0'; var aa: '0' & `${number}`; + + // Remove string literals from unions with matching template literals + + let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo' + let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo' + let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo` + + var bb: `${number}`; + var bb: `${number}` | '0'; \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralTypesPatterns.js b/tests/baselines/reference/templateLiteralTypesPatterns.js index 09df226a97b9e..8dc75965dd703 100644 --- a/tests/baselines/reference/templateLiteralTypesPatterns.js +++ b/tests/baselines/reference/templateLiteralTypesPatterns.js @@ -165,6 +165,15 @@ const exampleGood: B = "1 2"; // ok var aa: '0'; var aa: '0' & `${number}`; + +// Remove string literals from unions with matching template literals + +let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo' +let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo' +let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo` + +var bb: `${number}`; +var bb: `${number}` | '0'; //// [templateLiteralTypesPatterns.js] @@ -289,3 +298,9 @@ var exampleGood = "1 2"; // ok // Repro from #41161 var aa; var aa; +// Remove string literals from unions with matching template literals +var t1; // `foo${string}` | '1foo' +var t2; // `foo${string}` | '1foo' | 'xfoo' +var t3; // `foo${string}` | xfoo' | `${number}foo` +var bb; +var bb; diff --git a/tests/baselines/reference/templateLiteralTypesPatterns.symbols b/tests/baselines/reference/templateLiteralTypesPatterns.symbols index 8c1f1739fae68..605543882a213 100644 --- a/tests/baselines/reference/templateLiteralTypesPatterns.symbols +++ b/tests/baselines/reference/templateLiteralTypesPatterns.symbols @@ -401,3 +401,20 @@ var aa: '0'; var aa: '0' & `${number}`; >aa : Symbol(aa, Decl(templateLiteralTypesPatterns.ts, 164, 3), Decl(templateLiteralTypesPatterns.ts, 165, 3)) +// Remove string literals from unions with matching template literals + +let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo' +>t1 : Symbol(t1, Decl(templateLiteralTypesPatterns.ts, 169, 3)) + +let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo' +>t2 : Symbol(t2, Decl(templateLiteralTypesPatterns.ts, 170, 3)) + +let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo` +>t3 : Symbol(t3, Decl(templateLiteralTypesPatterns.ts, 171, 3)) + +var bb: `${number}`; +>bb : Symbol(bb, Decl(templateLiteralTypesPatterns.ts, 173, 3), Decl(templateLiteralTypesPatterns.ts, 174, 3)) + +var bb: `${number}` | '0'; +>bb : Symbol(bb, Decl(templateLiteralTypesPatterns.ts, 173, 3), Decl(templateLiteralTypesPatterns.ts, 174, 3)) + diff --git a/tests/baselines/reference/templateLiteralTypesPatterns.types b/tests/baselines/reference/templateLiteralTypesPatterns.types index ad27b431f4b0a..bdfe13262d82c 100644 --- a/tests/baselines/reference/templateLiteralTypesPatterns.types +++ b/tests/baselines/reference/templateLiteralTypesPatterns.types @@ -560,3 +560,20 @@ var aa: '0'; var aa: '0' & `${number}`; >aa : "0" +// Remove string literals from unions with matching template literals + +let t1: `foo${string}` | 'foo1' | '1foo'; // `foo${string}` | '1foo' +>t1 : `foo${string}` | "1foo" + +let t2: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo'; // `foo${string}` | '1foo' | 'xfoo' +>t2 : `foo${string}` | "1foo" | "xfoo" + +let t3: `foo1` | '1foo' | 'foofoo' | `foo${string}` | 'foox' | 'xfoo' | `${number}foo`; // `foo${string}` | xfoo' | `${number}foo` +>t3 : `foo${string}` | "xfoo" | `${number}foo` + +var bb: `${number}`; +>bb : `${number}` + +var bb: `${number}` | '0'; +>bb : `${number}` +