From 2a7eb58589c62529c9045c50cae31ddcca23a6ae Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 6 Dec 2021 10:51:50 -0800 Subject: [PATCH] Properly union inferred template literal string types (#46782) * Template literals and string mappings have 'string' as base type * Add regression test * Add tests for generic template literals * One more test --- src/compiler/checker.ts | 2 +- .../templateLiteralTypes3.errors.txt | 19 +++++ .../reference/templateLiteralTypes3.js | 32 ++++++++ .../reference/templateLiteralTypes3.symbols | 64 +++++++++++++++ .../reference/templateLiteralTypes3.types | 81 +++++++++++++++++++ .../types/literal/templateLiteralTypes3.ts | 19 +++++ 6 files changed, 216 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a8127a2689..56a36f658d7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20763,7 +20763,7 @@ namespace ts { function getBaseTypeOfLiteralType(type: Type): Type { return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(type as LiteralType) : - type.flags & TypeFlags.StringLiteral ? stringType : + type.flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ? stringType : type.flags & TypeFlags.NumberLiteral ? numberType : type.flags & TypeFlags.BigIntLiteral ? bigintType : type.flags & TypeFlags.BooleanLiteral ? booleanType : diff --git a/tests/baselines/reference/templateLiteralTypes3.errors.txt b/tests/baselines/reference/templateLiteralTypes3.errors.txt index 4c6e98d9365..d9e2608ac88 100644 --- a/tests/baselines/reference/templateLiteralTypes3.errors.txt +++ b/tests/baselines/reference/templateLiteralTypes3.errors.txt @@ -198,4 +198,23 @@ tests/cases/conformance/types/literal/templateLiteralTypes3.ts(141,9): error TS2 action.response; } } + + // Repro from #46768 + + type DotString = `${string}.${string}.${string}`; + + declare function noSpread

(args: P[]): P; + declare function spread

(...args: P[]): P; + + noSpread([`1.${'2'}.3`, `1.${'2'}.4`]); + noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]); + + spread(`1.${'2'}.3`, `1.${'2'}.4`); + spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`); + + function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) { + spread(`1.${t}.3`, `1.${t}.4`); + spread(`1.${u}.3`, `1.${u}.4`); + spread(u1, u2); + } \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralTypes3.js b/tests/baselines/reference/templateLiteralTypes3.js index fc2df68a400..2a6056a2b3f 100644 --- a/tests/baselines/reference/templateLiteralTypes3.js +++ b/tests/baselines/reference/templateLiteralTypes3.js @@ -170,6 +170,25 @@ function reducer(action: Action) { action.response; } } + +// Repro from #46768 + +type DotString = `${string}.${string}.${string}`; + +declare function noSpread

(args: P[]): P; +declare function spread

(...args: P[]): P; + +noSpread([`1.${'2'}.3`, `1.${'2'}.4`]); +noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]); + +spread(`1.${'2'}.3`, `1.${'2'}.4`); +spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`); + +function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) { + spread(`1.${t}.3`, `1.${t}.4`); + spread(`1.${u}.3`, `1.${u}.4`); + spread(u1, u2); +} //// [templateLiteralTypes3.js] @@ -257,6 +276,15 @@ function reducer(action) { action.response; } } +noSpread(["1.".concat('2', ".3"), "1.".concat('2', ".4")]); +noSpread(["1.".concat('2', ".3"), "1.".concat('2', ".4")]); +spread("1.".concat('2', ".3"), "1.".concat('2', ".4")); +spread("1.".concat('2', ".3"), "1.".concat('2', ".4")); +function ft1(t, u, u1, u2) { + spread("1.".concat(t, ".3"), "1.".concat(t, ".4")); + spread("1.".concat(u, ".3"), "1.".concat(u, ".4")); + spread(u1, u2); +} //// [templateLiteralTypes3.d.ts] @@ -324,3 +352,7 @@ declare type Action = { response: string; }; declare function reducer(action: Action): void; +declare type DotString = `${string}.${string}.${string}`; +declare function noSpread

(args: P[]): P; +declare function spread

(...args: P[]): P; +declare function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>): void; diff --git a/tests/baselines/reference/templateLiteralTypes3.symbols b/tests/baselines/reference/templateLiteralTypes3.symbols index 570a85a809d..47f4ee7fe6e 100644 --- a/tests/baselines/reference/templateLiteralTypes3.symbols +++ b/tests/baselines/reference/templateLiteralTypes3.symbols @@ -516,3 +516,67 @@ function reducer(action: Action) { } } +// Repro from #46768 + +type DotString = `${string}.${string}.${string}`; +>DotString : Symbol(DotString, Decl(templateLiteralTypes3.ts, 170, 1)) + +declare function noSpread

(args: P[]): P; +>noSpread : Symbol(noSpread, Decl(templateLiteralTypes3.ts, 174, 49)) +>P : Symbol(P, Decl(templateLiteralTypes3.ts, 176, 26)) +>DotString : Symbol(DotString, Decl(templateLiteralTypes3.ts, 170, 1)) +>args : Symbol(args, Decl(templateLiteralTypes3.ts, 176, 47)) +>P : Symbol(P, Decl(templateLiteralTypes3.ts, 176, 26)) +>P : Symbol(P, Decl(templateLiteralTypes3.ts, 176, 26)) + +declare function spread

(...args: P[]): P; +>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61)) +>P : Symbol(P, Decl(templateLiteralTypes3.ts, 177, 24)) +>DotString : Symbol(DotString, Decl(templateLiteralTypes3.ts, 170, 1)) +>args : Symbol(args, Decl(templateLiteralTypes3.ts, 177, 45)) +>P : Symbol(P, Decl(templateLiteralTypes3.ts, 177, 24)) +>P : Symbol(P, Decl(templateLiteralTypes3.ts, 177, 24)) + +noSpread([`1.${'2'}.3`, `1.${'2'}.4`]); +>noSpread : Symbol(noSpread, Decl(templateLiteralTypes3.ts, 174, 49)) + +noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]); +>noSpread : Symbol(noSpread, Decl(templateLiteralTypes3.ts, 174, 49)) + +spread(`1.${'2'}.3`, `1.${'2'}.4`); +>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61)) + +spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`); +>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61)) + +function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) { +>ft1 : Symbol(ft1, Decl(templateLiteralTypes3.ts, 183, 55)) +>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13)) +>t : Symbol(t, Decl(templateLiteralTypes3.ts, 185, 31)) +>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13)) +>u : Symbol(u, Decl(templateLiteralTypes3.ts, 185, 36)) +>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13)) +>u1 : Symbol(u1, Decl(templateLiteralTypes3.ts, 185, 53)) +>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13)) +>u2 : Symbol(u2, Decl(templateLiteralTypes3.ts, 185, 80)) +>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13)) + + spread(`1.${t}.3`, `1.${t}.4`); +>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61)) +>t : Symbol(t, Decl(templateLiteralTypes3.ts, 185, 31)) +>t : Symbol(t, Decl(templateLiteralTypes3.ts, 185, 31)) + + spread(`1.${u}.3`, `1.${u}.4`); +>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61)) +>u : Symbol(u, Decl(templateLiteralTypes3.ts, 185, 36)) +>u : Symbol(u, Decl(templateLiteralTypes3.ts, 185, 36)) + + spread(u1, u2); +>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61)) +>u1 : Symbol(u1, Decl(templateLiteralTypes3.ts, 185, 53)) +>u2 : Symbol(u2, Decl(templateLiteralTypes3.ts, 185, 80)) +} + diff --git a/tests/baselines/reference/templateLiteralTypes3.types b/tests/baselines/reference/templateLiteralTypes3.types index 5a8f15f27fb..27d780c7e22 100644 --- a/tests/baselines/reference/templateLiteralTypes3.types +++ b/tests/baselines/reference/templateLiteralTypes3.types @@ -513,3 +513,84 @@ function reducer(action: Action) { } } +// Repro from #46768 + +type DotString = `${string}.${string}.${string}`; +>DotString : `${string}.${string}.${string}` + +declare function noSpread

(args: P[]): P; +>noSpread :

(args: P[]) => P +>args : P[] + +declare function spread

(...args: P[]): P; +>spread :

(...args: P[]) => P +>args : P[] + +noSpread([`1.${'2'}.3`, `1.${'2'}.4`]); +>noSpread([`1.${'2'}.3`, `1.${'2'}.4`]) : "1.2.3" | "1.2.4" +>noSpread :

(args: P[]) => P +>[`1.${'2'}.3`, `1.${'2'}.4`] : ("1.2.3" | "1.2.4")[] +>`1.${'2'}.3` : "1.2.3" +>'2' : "2" +>`1.${'2'}.4` : "1.2.4" +>'2' : "2" + +noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]); +>noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]) : `1.${string}.3` | `1.${string}.4` +>noSpread :

(args: P[]) => P +>[`1.${'2' as string}.3`, `1.${'2' as string}.4`] : (`1.${string}.3` | `1.${string}.4`)[] +>`1.${'2' as string}.3` : `1.${string}.3` +>'2' as string : string +>'2' : "2" +>`1.${'2' as string}.4` : `1.${string}.4` +>'2' as string : string +>'2' : "2" + +spread(`1.${'2'}.3`, `1.${'2'}.4`); +>spread(`1.${'2'}.3`, `1.${'2'}.4`) : "1.2.3" | "1.2.4" +>spread :

(...args: P[]) => P +>`1.${'2'}.3` : "1.2.3" +>'2' : "2" +>`1.${'2'}.4` : "1.2.4" +>'2' : "2" + +spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`); +>spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`) : `1.${string}.3` | `1.${string}.4` +>spread :

(...args: P[]) => P +>`1.${'2' as string}.3` : `1.${string}.3` +>'2' as string : string +>'2' : "2" +>`1.${'2' as string}.4` : `1.${string}.4` +>'2' as string : string +>'2' : "2" + +function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) { +>ft1 : (t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) => void +>t : T +>u : Uppercase +>u1 : Uppercase<`1.${T}.3`> +>u2 : Uppercase<`1.${T}.4`> + + spread(`1.${t}.3`, `1.${t}.4`); +>spread(`1.${t}.3`, `1.${t}.4`) : `1.${T}.3` | `1.${T}.4` +>spread :

(...args: P[]) => P +>`1.${t}.3` : `1.${T}.3` +>t : T +>`1.${t}.4` : `1.${T}.4` +>t : T + + spread(`1.${u}.3`, `1.${u}.4`); +>spread(`1.${u}.3`, `1.${u}.4`) : `1.${Uppercase}.3` | `1.${Uppercase}.4` +>spread :

(...args: P[]) => P +>`1.${u}.3` : `1.${Uppercase}.3` +>u : Uppercase +>`1.${u}.4` : `1.${Uppercase}.4` +>u : Uppercase + + spread(u1, u2); +>spread(u1, u2) : Uppercase<`1.${T}.3`> | Uppercase<`1.${T}.4`> +>spread :

(...args: P[]) => P +>u1 : Uppercase<`1.${T}.3`> +>u2 : Uppercase<`1.${T}.4`> +} + diff --git a/tests/cases/conformance/types/literal/templateLiteralTypes3.ts b/tests/cases/conformance/types/literal/templateLiteralTypes3.ts index fbeae4b3f65..8c47fff8cb2 100644 --- a/tests/cases/conformance/types/literal/templateLiteralTypes3.ts +++ b/tests/cases/conformance/types/literal/templateLiteralTypes3.ts @@ -172,3 +172,22 @@ function reducer(action: Action) { action.response; } } + +// Repro from #46768 + +type DotString = `${string}.${string}.${string}`; + +declare function noSpread

(args: P[]): P; +declare function spread

(...args: P[]): P; + +noSpread([`1.${'2'}.3`, `1.${'2'}.4`]); +noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]); + +spread(`1.${'2'}.3`, `1.${'2'}.4`); +spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`); + +function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) { + spread(`1.${t}.3`, `1.${t}.4`); + spread(`1.${u}.3`, `1.${u}.4`); + spread(u1, u2); +}