Skip to content

Remove string literals from unions with matching template literals #41276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 27, 2020
Merged
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
17 changes: 17 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

15 changes: 15 additions & 0 deletions tests/baselines/reference/templateLiteralTypesPatterns.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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;
17 changes: 17 additions & 0 deletions tests/baselines/reference/templateLiteralTypesPatterns.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -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))

17 changes: 17 additions & 0 deletions tests/baselines/reference/templateLiteralTypesPatterns.types
Original file line number Diff line number Diff line change
Expand Up @@ -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}`

Original file line number Diff line number Diff line change
Expand Up @@ -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';