Skip to content

Commit 2a7eb58

Browse files
authored
Properly union inferred template literal string types (microsoft#46782)
* Template literals and string mappings have 'string' as base type * Add regression test * Add tests for generic template literals * One more test
1 parent 7a1687d commit 2a7eb58

File tree

6 files changed

+216
-1
lines changed

6 files changed

+216
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20763,7 +20763,7 @@ namespace ts {
2076320763

2076420764
function getBaseTypeOfLiteralType(type: Type): Type {
2076520765
return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(type as LiteralType) :
20766-
type.flags & TypeFlags.StringLiteral ? stringType :
20766+
type.flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ? stringType :
2076720767
type.flags & TypeFlags.NumberLiteral ? numberType :
2076820768
type.flags & TypeFlags.BigIntLiteral ? bigintType :
2076920769
type.flags & TypeFlags.BooleanLiteral ? booleanType :

tests/baselines/reference/templateLiteralTypes3.errors.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,23 @@ tests/cases/conformance/types/literal/templateLiteralTypes3.ts(141,9): error TS2
198198
action.response;
199199
}
200200
}
201+
202+
// Repro from #46768
203+
204+
type DotString = `${string}.${string}.${string}`;
205+
206+
declare function noSpread<P extends DotString>(args: P[]): P;
207+
declare function spread<P extends DotString>(...args: P[]): P;
208+
209+
noSpread([`1.${'2'}.3`, `1.${'2'}.4`]);
210+
noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]);
211+
212+
spread(`1.${'2'}.3`, `1.${'2'}.4`);
213+
spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`);
214+
215+
function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) {
216+
spread(`1.${t}.3`, `1.${t}.4`);
217+
spread(`1.${u}.3`, `1.${u}.4`);
218+
spread(u1, u2);
219+
}
201220

tests/baselines/reference/templateLiteralTypes3.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,25 @@ function reducer(action: Action) {
170170
action.response;
171171
}
172172
}
173+
174+
// Repro from #46768
175+
176+
type DotString = `${string}.${string}.${string}`;
177+
178+
declare function noSpread<P extends DotString>(args: P[]): P;
179+
declare function spread<P extends DotString>(...args: P[]): P;
180+
181+
noSpread([`1.${'2'}.3`, `1.${'2'}.4`]);
182+
noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]);
183+
184+
spread(`1.${'2'}.3`, `1.${'2'}.4`);
185+
spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`);
186+
187+
function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) {
188+
spread(`1.${t}.3`, `1.${t}.4`);
189+
spread(`1.${u}.3`, `1.${u}.4`);
190+
spread(u1, u2);
191+
}
173192

174193

175194
//// [templateLiteralTypes3.js]
@@ -257,6 +276,15 @@ function reducer(action) {
257276
action.response;
258277
}
259278
}
279+
noSpread(["1.".concat('2', ".3"), "1.".concat('2', ".4")]);
280+
noSpread(["1.".concat('2', ".3"), "1.".concat('2', ".4")]);
281+
spread("1.".concat('2', ".3"), "1.".concat('2', ".4"));
282+
spread("1.".concat('2', ".3"), "1.".concat('2', ".4"));
283+
function ft1(t, u, u1, u2) {
284+
spread("1.".concat(t, ".3"), "1.".concat(t, ".4"));
285+
spread("1.".concat(u, ".3"), "1.".concat(u, ".4"));
286+
spread(u1, u2);
287+
}
260288

261289

262290
//// [templateLiteralTypes3.d.ts]
@@ -324,3 +352,7 @@ declare type Action = {
324352
response: string;
325353
};
326354
declare function reducer(action: Action): void;
355+
declare type DotString = `${string}.${string}.${string}`;
356+
declare function noSpread<P extends DotString>(args: P[]): P;
357+
declare function spread<P extends DotString>(...args: P[]): P;
358+
declare function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>): void;

tests/baselines/reference/templateLiteralTypes3.symbols

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,3 +516,67 @@ function reducer(action: Action) {
516516
}
517517
}
518518

519+
// Repro from #46768
520+
521+
type DotString = `${string}.${string}.${string}`;
522+
>DotString : Symbol(DotString, Decl(templateLiteralTypes3.ts, 170, 1))
523+
524+
declare function noSpread<P extends DotString>(args: P[]): P;
525+
>noSpread : Symbol(noSpread, Decl(templateLiteralTypes3.ts, 174, 49))
526+
>P : Symbol(P, Decl(templateLiteralTypes3.ts, 176, 26))
527+
>DotString : Symbol(DotString, Decl(templateLiteralTypes3.ts, 170, 1))
528+
>args : Symbol(args, Decl(templateLiteralTypes3.ts, 176, 47))
529+
>P : Symbol(P, Decl(templateLiteralTypes3.ts, 176, 26))
530+
>P : Symbol(P, Decl(templateLiteralTypes3.ts, 176, 26))
531+
532+
declare function spread<P extends DotString>(...args: P[]): P;
533+
>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61))
534+
>P : Symbol(P, Decl(templateLiteralTypes3.ts, 177, 24))
535+
>DotString : Symbol(DotString, Decl(templateLiteralTypes3.ts, 170, 1))
536+
>args : Symbol(args, Decl(templateLiteralTypes3.ts, 177, 45))
537+
>P : Symbol(P, Decl(templateLiteralTypes3.ts, 177, 24))
538+
>P : Symbol(P, Decl(templateLiteralTypes3.ts, 177, 24))
539+
540+
noSpread([`1.${'2'}.3`, `1.${'2'}.4`]);
541+
>noSpread : Symbol(noSpread, Decl(templateLiteralTypes3.ts, 174, 49))
542+
543+
noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]);
544+
>noSpread : Symbol(noSpread, Decl(templateLiteralTypes3.ts, 174, 49))
545+
546+
spread(`1.${'2'}.3`, `1.${'2'}.4`);
547+
>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61))
548+
549+
spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`);
550+
>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61))
551+
552+
function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) {
553+
>ft1 : Symbol(ft1, Decl(templateLiteralTypes3.ts, 183, 55))
554+
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13))
555+
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 185, 31))
556+
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13))
557+
>u : Symbol(u, Decl(templateLiteralTypes3.ts, 185, 36))
558+
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --))
559+
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13))
560+
>u1 : Symbol(u1, Decl(templateLiteralTypes3.ts, 185, 53))
561+
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --))
562+
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13))
563+
>u2 : Symbol(u2, Decl(templateLiteralTypes3.ts, 185, 80))
564+
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --))
565+
>T : Symbol(T, Decl(templateLiteralTypes3.ts, 185, 13))
566+
567+
spread(`1.${t}.3`, `1.${t}.4`);
568+
>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61))
569+
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 185, 31))
570+
>t : Symbol(t, Decl(templateLiteralTypes3.ts, 185, 31))
571+
572+
spread(`1.${u}.3`, `1.${u}.4`);
573+
>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61))
574+
>u : Symbol(u, Decl(templateLiteralTypes3.ts, 185, 36))
575+
>u : Symbol(u, Decl(templateLiteralTypes3.ts, 185, 36))
576+
577+
spread(u1, u2);
578+
>spread : Symbol(spread, Decl(templateLiteralTypes3.ts, 176, 61))
579+
>u1 : Symbol(u1, Decl(templateLiteralTypes3.ts, 185, 53))
580+
>u2 : Symbol(u2, Decl(templateLiteralTypes3.ts, 185, 80))
581+
}
582+

tests/baselines/reference/templateLiteralTypes3.types

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,3 +513,84 @@ function reducer(action: Action) {
513513
}
514514
}
515515

516+
// Repro from #46768
517+
518+
type DotString = `${string}.${string}.${string}`;
519+
>DotString : `${string}.${string}.${string}`
520+
521+
declare function noSpread<P extends DotString>(args: P[]): P;
522+
>noSpread : <P extends `${string}.${string}.${string}`>(args: P[]) => P
523+
>args : P[]
524+
525+
declare function spread<P extends DotString>(...args: P[]): P;
526+
>spread : <P extends `${string}.${string}.${string}`>(...args: P[]) => P
527+
>args : P[]
528+
529+
noSpread([`1.${'2'}.3`, `1.${'2'}.4`]);
530+
>noSpread([`1.${'2'}.3`, `1.${'2'}.4`]) : "1.2.3" | "1.2.4"
531+
>noSpread : <P extends `${string}.${string}.${string}`>(args: P[]) => P
532+
>[`1.${'2'}.3`, `1.${'2'}.4`] : ("1.2.3" | "1.2.4")[]
533+
>`1.${'2'}.3` : "1.2.3"
534+
>'2' : "2"
535+
>`1.${'2'}.4` : "1.2.4"
536+
>'2' : "2"
537+
538+
noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]);
539+
>noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]) : `1.${string}.3` | `1.${string}.4`
540+
>noSpread : <P extends `${string}.${string}.${string}`>(args: P[]) => P
541+
>[`1.${'2' as string}.3`, `1.${'2' as string}.4`] : (`1.${string}.3` | `1.${string}.4`)[]
542+
>`1.${'2' as string}.3` : `1.${string}.3`
543+
>'2' as string : string
544+
>'2' : "2"
545+
>`1.${'2' as string}.4` : `1.${string}.4`
546+
>'2' as string : string
547+
>'2' : "2"
548+
549+
spread(`1.${'2'}.3`, `1.${'2'}.4`);
550+
>spread(`1.${'2'}.3`, `1.${'2'}.4`) : "1.2.3" | "1.2.4"
551+
>spread : <P extends `${string}.${string}.${string}`>(...args: P[]) => P
552+
>`1.${'2'}.3` : "1.2.3"
553+
>'2' : "2"
554+
>`1.${'2'}.4` : "1.2.4"
555+
>'2' : "2"
556+
557+
spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`);
558+
>spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`) : `1.${string}.3` | `1.${string}.4`
559+
>spread : <P extends `${string}.${string}.${string}`>(...args: P[]) => P
560+
>`1.${'2' as string}.3` : `1.${string}.3`
561+
>'2' as string : string
562+
>'2' : "2"
563+
>`1.${'2' as string}.4` : `1.${string}.4`
564+
>'2' as string : string
565+
>'2' : "2"
566+
567+
function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) {
568+
>ft1 : <T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) => void
569+
>t : T
570+
>u : Uppercase<T>
571+
>u1 : Uppercase<`1.${T}.3`>
572+
>u2 : Uppercase<`1.${T}.4`>
573+
574+
spread(`1.${t}.3`, `1.${t}.4`);
575+
>spread(`1.${t}.3`, `1.${t}.4`) : `1.${T}.3` | `1.${T}.4`
576+
>spread : <P extends `${string}.${string}.${string}`>(...args: P[]) => P
577+
>`1.${t}.3` : `1.${T}.3`
578+
>t : T
579+
>`1.${t}.4` : `1.${T}.4`
580+
>t : T
581+
582+
spread(`1.${u}.3`, `1.${u}.4`);
583+
>spread(`1.${u}.3`, `1.${u}.4`) : `1.${Uppercase<T>}.3` | `1.${Uppercase<T>}.4`
584+
>spread : <P extends `${string}.${string}.${string}`>(...args: P[]) => P
585+
>`1.${u}.3` : `1.${Uppercase<T>}.3`
586+
>u : Uppercase<T>
587+
>`1.${u}.4` : `1.${Uppercase<T>}.4`
588+
>u : Uppercase<T>
589+
590+
spread(u1, u2);
591+
>spread(u1, u2) : Uppercase<`1.${T}.3`> | Uppercase<`1.${T}.4`>
592+
>spread : <P extends `${string}.${string}.${string}`>(...args: P[]) => P
593+
>u1 : Uppercase<`1.${T}.3`>
594+
>u2 : Uppercase<`1.${T}.4`>
595+
}
596+

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,22 @@ function reducer(action: Action) {
172172
action.response;
173173
}
174174
}
175+
176+
// Repro from #46768
177+
178+
type DotString = `${string}.${string}.${string}`;
179+
180+
declare function noSpread<P extends DotString>(args: P[]): P;
181+
declare function spread<P extends DotString>(...args: P[]): P;
182+
183+
noSpread([`1.${'2'}.3`, `1.${'2'}.4`]);
184+
noSpread([`1.${'2' as string}.3`, `1.${'2' as string}.4`]);
185+
186+
spread(`1.${'2'}.3`, `1.${'2'}.4`);
187+
spread(`1.${'2' as string}.3`, `1.${'2' as string}.4`);
188+
189+
function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) {
190+
spread(`1.${t}.3`, `1.${t}.4`);
191+
spread(`1.${u}.3`, `1.${u}.4`);
192+
spread(u1, u2);
193+
}

0 commit comments

Comments
 (0)