Skip to content

Commit 658764e

Browse files
authored
Bypass caching in removeStringLiteralsMatchedByTemplateLiterals (#46525)
* Bypass caching in removeStringLiteralsMatchedByTemplateLiterals * Add regression test
1 parent e1a2c2c commit 658764e

File tree

6 files changed

+129
-242
lines changed

6 files changed

+129
-242
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14203,13 +14203,13 @@ namespace ts {
1420314203
}
1420414204

1420514205
function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) {
14206-
const templates = filter(types, isPatternLiteralType);
14206+
const templates = filter(types, isPatternLiteralType) as TemplateLiteralType[];
1420714207
if (templates.length) {
1420814208
let i = types.length;
1420914209
while (i > 0) {
1421014210
i--;
1421114211
const t = types[i];
14212-
if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeSubtypeOf(t, template))) {
14212+
if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeMatchedByTemplateLiteralType(t, template))) {
1421314213
orderedRemoveItemAt(types, i);
1421414214
}
1421514215
}
@@ -19090,8 +19090,7 @@ namespace ts {
1909019090
// For example, `foo-${number}` is related to `foo-${string}` even though number isn't related to string.
1909119091
instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers));
1909219092
}
19093-
const result = inferTypesFromTemplateLiteralType(source, target as TemplateLiteralType);
19094-
if (result && every(result, (r, i) => isValidTypeForTemplateLiteralPlaceholder(r, (target as TemplateLiteralType).types[i]))) {
19093+
if (isTypeMatchedByTemplateLiteralType(source, target as TemplateLiteralType)) {
1909519094
return Ternary.True;
1909619095
}
1909719096
}
@@ -21561,6 +21560,11 @@ namespace ts {
2156121560
undefined;
2156221561
}
2156321562

21563+
function isTypeMatchedByTemplateLiteralType(source: Type, target: TemplateLiteralType): boolean {
21564+
const inferences = inferTypesFromTemplateLiteralType(source, target);
21565+
return !!inferences && every(inferences, (r, i) => isValidTypeForTemplateLiteralPlaceholder(r, target.types[i]));
21566+
}
21567+
2156421568
function getStringLikeTypeForType(type: Type) {
2156521569
return type.flags & (TypeFlags.Any | TypeFlags.StringLike) ? type : getTemplateLiteralType(["", ""], [type]);
2156621570
}

tests/baselines/reference/templateLiteralTypes1.errors.txt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ tests/cases/conformance/types/literal/templateLiteralTypes1.ts(165,15): error TS
66
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(197,16): error TS2590: Expression produces a union type that is too complex to represent.
77
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(201,16): error TS2590: Expression produces a union type that is too complex to represent.
88
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(205,16): error TS2590: Expression produces a union type that is too complex to represent.
9+
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(251,7): error TS2590: Expression produces a union type that is too complex to represent.
910

1011

11-
==== tests/cases/conformance/types/literal/templateLiteralTypes1.ts (6 errors) ====
12+
==== tests/cases/conformance/types/literal/templateLiteralTypes1.ts (7 errors) ====
1213
// Template types example from #12754
1314

1415
const createScopedActionType = <S extends string>(scope: S) => <T extends string>(type: T) => `${scope}/${type}` as `${S}/${T}`;
@@ -259,4 +260,23 @@ tests/cases/conformance/types/literal/templateLiteralTypes1.ts(205,16): error TS
259260
} as const;
260261

261262
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
263+
264+
// Repro from #46480
265+
266+
export type Spacing =
267+
| `0`
268+
| `${number}px`
269+
| `${number}rem`
270+
| `s${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20}`;
271+
272+
const spacing: Spacing = "s12"
273+
274+
export type SpacingShorthand =
275+
| `${Spacing} ${Spacing}`
276+
| `${Spacing} ${Spacing} ${Spacing}`
277+
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
278+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
279+
!!! error TS2590: Expression produces a union type that is too complex to represent.
280+
281+
const test1: SpacingShorthand = "0 0 0";
262282

tests/baselines/reference/templateLiteralTypes1.js

Lines changed: 22 additions & 237 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,29 @@ const obj2 = {
235235
} as const;
236236
237237
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
238+
239+
// Repro from #46480
240+
241+
export type Spacing =
242+
| `0`
243+
| `${number}px`
244+
| `${number}rem`
245+
| `s${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20}`;
246+
247+
const spacing: Spacing = "s12"
248+
249+
export type SpacingShorthand =
250+
| `${Spacing} ${Spacing}`
251+
| `${Spacing} ${Spacing} ${Spacing}`
252+
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
253+
254+
const test1: SpacingShorthand = "0 0 0";
238255

239256

240257
//// [templateLiteralTypes1.js]
241258
"use strict";
242259
// Template types example from #12754
260+
exports.__esModule = true;
243261
var createScopedActionType = function (scope) { return function (type) { return "".concat(scope, "/").concat(type); }; };
244262
var createActionInMyScope = createScopedActionType("MyScope"); // <T extends string>(type: T) => `MyScope/${T}`
245263
var MY_ACTION = createActionInMyScope("MY_ACTION"); // 'MyScope/MY_ACTION'
@@ -274,243 +292,10 @@ var obj2 = {
274292
]
275293
};
276294
var make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
295+
var spacing = "s12";
296+
var test1 = "0 0 0";
277297

278298

279299
//// [templateLiteralTypes1.d.ts]
280-
declare const createScopedActionType: <S extends string>(scope: S) => <T extends string>(type: T) => `${S}/${T}`;
281-
declare const createActionInMyScope: <T extends string>(type: T) => `MyScope/${T}`;
282-
declare const MY_ACTION: "MyScope/MY_ACTION";
283-
declare type EventName<S extends string> = `${S}Changed`;
284-
declare type EN1 = EventName<'Foo' | 'Bar' | 'Baz'>;
285-
declare type Loc = `${'top' | 'middle' | 'bottom'}-${'left' | 'center' | 'right'}`;
286-
declare type ToString<T extends string | number | boolean | bigint> = `${T}`;
287-
declare type TS1 = ToString<'abc' | 42 | true | -1234n>;
288-
declare type TL1<T extends string> = `a${T}b${T}c`;
289-
declare type TL2<U extends string> = TL1<`x${U}y`>;
290-
declare type TL3 = TL2<'o'>;
291-
declare type Cases<T extends string> = `${Uppercase<T>} ${Lowercase<T>} ${Capitalize<T>} ${Uncapitalize<T>}`;
292-
declare type TCA1 = Cases<'bar'>;
293-
declare type TCA2 = Cases<'BAR'>;
294-
declare function test<T extends 'foo' | 'bar'>(name: `get${Capitalize<T>}`): void;
295-
declare function fa1<T>(x: T, y: {
296-
[P in keyof T]: T[P];
297-
}, z: {
298-
[P in keyof T & string as `p_${P}`]: T[P];
299-
}): void;
300-
declare function fa2<T, U extends T, A extends string, B extends A>(x: {
301-
[P in B as `p_${P}`]: T;
302-
}, y: {
303-
[Q in A as `p_${Q}`]: U;
304-
}): void;
305-
declare type Join<T extends unknown[], D extends string> = T extends [] ? '' : T extends [string | number | boolean | bigint] ? `${T[0]}` : T extends [string | number | boolean | bigint, ...infer U] ? `${T[0]}${D}${Join<U, D>}` : string;
306-
declare type TJ1 = Join<[1, 2, 3, 4], '.'>;
307-
declare type TJ2 = Join<['foo', 'bar', 'baz'], '-'>;
308-
declare type TJ3 = Join<[], '.'>;
309-
declare type MatchPair<S extends string> = S extends `[${infer A},${infer B}]` ? [A, B] : unknown;
310-
declare type T20 = MatchPair<'[1,2]'>;
311-
declare type T21 = MatchPair<'[foo,bar]'>;
312-
declare type T22 = MatchPair<' [1,2]'>;
313-
declare type T23 = MatchPair<'[123]'>;
314-
declare type T24 = MatchPair<'[1,2,3,4]'>;
315-
declare type SnakeToCamelCase<S extends string> = S extends `${infer T}_${infer U}` ? `${Lowercase<T>}${SnakeToPascalCase<U>}` : S extends `${infer T}` ? `${Lowercase<T>}` : SnakeToPascalCase<S>;
316-
declare type SnakeToPascalCase<S extends string> = string extends S ? string : S extends `${infer T}_${infer U}` ? `${Capitalize<Lowercase<T>>}${SnakeToPascalCase<U>}` : S extends `${infer T}` ? `${Capitalize<Lowercase<T>>}` : never;
317-
declare type RR0 = SnakeToPascalCase<'hello_world_foo'>;
318-
declare type RR1 = SnakeToPascalCase<'FOO_BAR_BAZ'>;
319-
declare type RR2 = SnakeToCamelCase<'hello_world_foo'>;
320-
declare type RR3 = SnakeToCamelCase<'FOO_BAR_BAZ'>;
321-
declare type FirstTwoAndRest<S extends string> = S extends `${infer A}${infer B}${infer R}` ? [`${A}${B}`, R] : unknown;
322-
declare type T25 = FirstTwoAndRest<'abcde'>;
323-
declare type T26 = FirstTwoAndRest<'ab'>;
324-
declare type T27 = FirstTwoAndRest<'a'>;
325-
declare type HexDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f';
326-
declare type HexColor<S extends string> = S extends `#${infer R1}${infer R2}${infer G1}${infer G2}${infer B1}${infer B2}` ? [
327-
R1,
328-
R2,
329-
G1,
330-
G2,
331-
B1,
332-
B2
333-
] extends [HexDigit, HexDigit, HexDigit, HexDigit, HexDigit, HexDigit] ? S : never : never;
334-
declare type TH1 = HexColor<'#8080FF'>;
335-
declare type TH2 = HexColor<'#80c0ff'>;
336-
declare type TH3 = HexColor<'#8080F'>;
337-
declare type TH4 = HexColor<'#8080FFF'>;
338-
declare type Trim<S extends string> = S extends ` ${infer T}` ? Trim<T> : S extends `${infer T} ` ? Trim<T> : S;
339-
declare type TR1 = Trim<'xx '>;
340-
declare type TR2 = Trim<' xx'>;
341-
declare type TR3 = Trim<' xx '>;
342-
declare type Split<S extends string, D extends string> = string extends S ? string[] : S extends '' ? [] : S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [
343-
S
344-
];
345-
declare type T40 = Split<'foo', '.'>;
346-
declare type T41 = Split<'foo.bar.baz', '.'>;
347-
declare type T42 = Split<'foo.bar', ''>;
348-
declare type T43 = Split<any, '.'>;
349-
declare function getProp<T, P0 extends keyof T & string, P1 extends keyof T[P0] & string, P2 extends keyof T[P0][P1] & string>(obj: T, path: `${P0}.${P1}.${P2}`): T[P0][P1][P2];
350-
declare function getProp<T, P0 extends keyof T & string, P1 extends keyof T[P0] & string>(obj: T, path: `${P0}.${P1}`): T[P0][P1];
351-
declare function getProp<T, P0 extends keyof T & string>(obj: T, path: P0): T[P0];
352-
declare function getProp(obj: object, path: string): unknown;
353-
declare let p1: {
354-
readonly b: {
355-
readonly c: 42;
356-
readonly d: "hello";
357-
};
358-
};
359-
declare let p2: {
360-
readonly c: 42;
361-
readonly d: "hello";
362-
};
363-
declare let p3: "hello";
364-
declare type PropType<T, Path extends string> = string extends Path ? unknown : Path extends keyof T ? T[Path] : Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown : unknown;
365-
declare function getPropValue<T, P extends string>(obj: T, path: P): PropType<T, P>;
366-
declare const s: string;
367-
declare const obj: {
368-
a: {
369-
b: {
370-
c: number;
371-
d: string;
372-
};
373-
};
374-
};
375-
declare type S1<T> = T extends `foo${infer U}bar` ? S2<U> : never;
376-
declare type S2<S extends string> = S;
377-
declare type TV1 = `${infer X}`;
378-
declare type Chars<S extends string> = string extends S ? string[] : S extends `${infer C0}${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer C6}${infer C7}${infer C8}${infer C9}${infer R}` ? [C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, ...Chars<R>] : S extends `${infer C}${infer R}` ? [C, ...Chars<R>] : S extends '' ? [] : never;
379-
declare type L1 = Chars<'FooBarBazThisIsALongerString'>;
380-
declare type Foo<T> = T extends `*${infer S}*` ? S : never;
381-
declare type TF1 = Foo<any>;
382-
declare type TF2 = Foo<string>;
383-
declare type TF3 = Foo<'abc'>;
384-
declare type TF4 = Foo<'*abc*'>;
385-
declare type A = any;
386-
declare type U1 = {
387-
a1: A;
388-
} | {
389-
b1: A;
390-
} | {
391-
c1: A;
392-
} | {
393-
d1: A;
394-
} | {
395-
e1: A;
396-
} | {
397-
f1: A;
398-
} | {
399-
g1: A;
400-
} | {
401-
h1: A;
402-
} | {
403-
i1: A;
404-
} | {
405-
j1: A;
406-
};
407-
declare type U2 = {
408-
a2: A;
409-
} | {
410-
b2: A;
411-
} | {
412-
c2: A;
413-
} | {
414-
d2: A;
415-
} | {
416-
e2: A;
417-
} | {
418-
f2: A;
419-
} | {
420-
g2: A;
421-
} | {
422-
h2: A;
423-
} | {
424-
i2: A;
425-
} | {
426-
j2: A;
427-
};
428-
declare type U3 = {
429-
a3: A;
430-
} | {
431-
b3: A;
432-
} | {
433-
c3: A;
434-
} | {
435-
d3: A;
436-
} | {
437-
e3: A;
438-
} | {
439-
f3: A;
440-
} | {
441-
g3: A;
442-
} | {
443-
h3: A;
444-
} | {
445-
i3: A;
446-
} | {
447-
j3: A;
448-
};
449-
declare type U4 = {
450-
a4: A;
451-
} | {
452-
b4: A;
453-
} | {
454-
c4: A;
455-
} | {
456-
d4: A;
457-
} | {
458-
e4: A;
459-
} | {
460-
f4: A;
461-
} | {
462-
g4: A;
463-
} | {
464-
h4: A;
465-
} | {
466-
i4: A;
467-
} | {
468-
j4: A;
469-
};
470-
declare type U5 = {
471-
a5: A;
472-
} | {
473-
b5: A;
474-
} | {
475-
c5: A;
476-
} | {
477-
d5: A;
478-
} | {
479-
e5: A;
480-
} | {
481-
f5: A;
482-
} | {
483-
g5: A;
484-
} | {
485-
h5: A;
486-
} | {
487-
i5: A;
488-
} | {
489-
j5: A;
490-
};
491-
declare type U100000 = U1 & U2 & U3 & U4 & U5;
492-
declare type Digits = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
493-
declare type D100000 = `${Digits}${Digits}${Digits}${Digits}${Digits}`;
494-
declare type TDigits = [0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9];
495-
declare type T100000 = [...TDigits, ...TDigits, ...TDigits, ...TDigits, ...TDigits];
496-
declare type IsNegative<T extends number> = `${T}` extends `-${string}` ? true : false;
497-
declare type AA<T extends number, Q extends number> = [
498-
true,
499-
true
500-
] extends [IsNegative<T>, IsNegative<Q>] ? 'Every thing is ok!' : ['strange', IsNegative<T>, IsNegative<Q>];
501-
declare type BB = AA<-2, -2>;
502-
declare type PathKeys<T> = T extends readonly any[] ? Extract<keyof T, `${number}`> | SubKeys<T, Extract<keyof T, `${number}`>> : T extends object ? Extract<keyof T, string> | SubKeys<T, Extract<keyof T, string>> : never;
503-
declare type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
504-
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
505-
declare const obj2: {
506-
readonly name: "John";
507-
readonly age: 42;
508-
readonly cars: readonly [{
509-
readonly make: "Ford";
510-
readonly age: 10;
511-
}, {
512-
readonly make: "Trabant";
513-
readonly age: 35;
514-
}];
515-
};
516-
declare let make: "Trabant";
300+
export declare type Spacing = `0` | `${number}px` | `${number}rem` | `s${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20}`;
301+
export declare type SpacingShorthand = `${Spacing} ${Spacing}` | `${Spacing} ${Spacing} ${Spacing}` | `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;

tests/baselines/reference/templateLiteralTypes1.symbols

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,3 +952,39 @@ let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
952952
>getProp2 : Symbol(getProp2, Decl(templateLiteralTypes1.ts, 222, 89))
953953
>obj2 : Symbol(obj2, Decl(templateLiteralTypes1.ts, 226, 5))
954954

955+
// Repro from #46480
956+
957+
export type Spacing =
958+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
959+
960+
| `0`
961+
| `${number}px`
962+
| `${number}rem`
963+
| `s${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20}`;
964+
965+
const spacing: Spacing = "s12"
966+
>spacing : Symbol(spacing, Decl(templateLiteralTypes1.ts, 245, 5))
967+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
968+
969+
export type SpacingShorthand =
970+
>SpacingShorthand : Symbol(SpacingShorthand, Decl(templateLiteralTypes1.ts, 245, 30))
971+
972+
| `${Spacing} ${Spacing}`
973+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
974+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
975+
976+
| `${Spacing} ${Spacing} ${Spacing}`
977+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
978+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
979+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
980+
981+
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
982+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
983+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
984+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
985+
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
986+
987+
const test1: SpacingShorthand = "0 0 0";
988+
>test1 : Symbol(test1, Decl(templateLiteralTypes1.ts, 252, 5))
989+
>SpacingShorthand : Symbol(SpacingShorthand, Decl(templateLiteralTypes1.ts, 245, 30))
990+

0 commit comments

Comments
 (0)