Bypass caching in removeStringLiteralsMatchedByTemplateLiterals (#46525)

* Bypass caching in removeStringLiteralsMatchedByTemplateLiterals

* Add regression test
This commit is contained in:
Anders Hejlsberg 2021-10-26 09:34:15 -07:00 committed by GitHub
parent e1a2c2c5a9
commit 658764e499
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 129 additions and 242 deletions

View file

@ -14203,13 +14203,13 @@ namespace ts {
}
function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) {
const templates = filter(types, isPatternLiteralType);
const templates = filter(types, isPatternLiteralType) as TemplateLiteralType[];
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))) {
if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeMatchedByTemplateLiteralType(t, template))) {
orderedRemoveItemAt(types, i);
}
}
@ -19090,8 +19090,7 @@ namespace ts {
// For example, `foo-${number}` is related to `foo-${string}` even though number isn't related to string.
instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers));
}
const result = inferTypesFromTemplateLiteralType(source, target as TemplateLiteralType);
if (result && every(result, (r, i) => isValidTypeForTemplateLiteralPlaceholder(r, (target as TemplateLiteralType).types[i]))) {
if (isTypeMatchedByTemplateLiteralType(source, target as TemplateLiteralType)) {
return Ternary.True;
}
}
@ -21561,6 +21560,11 @@ namespace ts {
undefined;
}
function isTypeMatchedByTemplateLiteralType(source: Type, target: TemplateLiteralType): boolean {
const inferences = inferTypesFromTemplateLiteralType(source, target);
return !!inferences && every(inferences, (r, i) => isValidTypeForTemplateLiteralPlaceholder(r, target.types[i]));
}
function getStringLikeTypeForType(type: Type) {
return type.flags & (TypeFlags.Any | TypeFlags.StringLike) ? type : getTemplateLiteralType(["", ""], [type]);
}

View file

@ -6,9 +6,10 @@ tests/cases/conformance/types/literal/templateLiteralTypes1.ts(165,15): error TS
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(197,16): error TS2590: Expression produces a union type that is too complex to represent.
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(201,16): error TS2590: Expression produces a union type that is too complex to represent.
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(205,16): error TS2590: Expression produces a union type that is too complex to represent.
tests/cases/conformance/types/literal/templateLiteralTypes1.ts(251,7): error TS2590: Expression produces a union type that is too complex to represent.
==== tests/cases/conformance/types/literal/templateLiteralTypes1.ts (6 errors) ====
==== tests/cases/conformance/types/literal/templateLiteralTypes1.ts (7 errors) ====
// Template types example from #12754
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
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
// Repro from #46480
export 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}`;
const spacing: Spacing = "s12"
export type SpacingShorthand =
| `${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2590: Expression produces a union type that is too complex to represent.
const test1: SpacingShorthand = "0 0 0";

View file

@ -235,11 +235,29 @@ const obj2 = {
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
// Repro from #46480
export 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}`;
const spacing: Spacing = "s12"
export type SpacingShorthand =
| `${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
const test1: SpacingShorthand = "0 0 0";
//// [templateLiteralTypes1.js]
"use strict";
// Template types example from #12754
exports.__esModule = true;
var createScopedActionType = function (scope) { return function (type) { return "".concat(scope, "/").concat(type); }; };
var createActionInMyScope = createScopedActionType("MyScope"); // <T extends string>(type: T) => `MyScope/${T}`
var MY_ACTION = createActionInMyScope("MY_ACTION"); // 'MyScope/MY_ACTION'
@ -274,243 +292,10 @@ var obj2 = {
]
};
var make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
var spacing = "s12";
var test1 = "0 0 0";
//// [templateLiteralTypes1.d.ts]
declare const createScopedActionType: <S extends string>(scope: S) => <T extends string>(type: T) => `${S}/${T}`;
declare const createActionInMyScope: <T extends string>(type: T) => `MyScope/${T}`;
declare const MY_ACTION: "MyScope/MY_ACTION";
declare type EventName<S extends string> = `${S}Changed`;
declare type EN1 = EventName<'Foo' | 'Bar' | 'Baz'>;
declare type Loc = `${'top' | 'middle' | 'bottom'}-${'left' | 'center' | 'right'}`;
declare type ToString<T extends string | number | boolean | bigint> = `${T}`;
declare type TS1 = ToString<'abc' | 42 | true | -1234n>;
declare type TL1<T extends string> = `a${T}b${T}c`;
declare type TL2<U extends string> = TL1<`x${U}y`>;
declare type TL3 = TL2<'o'>;
declare type Cases<T extends string> = `${Uppercase<T>} ${Lowercase<T>} ${Capitalize<T>} ${Uncapitalize<T>}`;
declare type TCA1 = Cases<'bar'>;
declare type TCA2 = Cases<'BAR'>;
declare function test<T extends 'foo' | 'bar'>(name: `get${Capitalize<T>}`): void;
declare function fa1<T>(x: T, y: {
[P in keyof T]: T[P];
}, z: {
[P in keyof T & string as `p_${P}`]: T[P];
}): void;
declare function fa2<T, U extends T, A extends string, B extends A>(x: {
[P in B as `p_${P}`]: T;
}, y: {
[Q in A as `p_${Q}`]: U;
}): void;
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;
declare type TJ1 = Join<[1, 2, 3, 4], '.'>;
declare type TJ2 = Join<['foo', 'bar', 'baz'], '-'>;
declare type TJ3 = Join<[], '.'>;
declare type MatchPair<S extends string> = S extends `[${infer A},${infer B}]` ? [A, B] : unknown;
declare type T20 = MatchPair<'[1,2]'>;
declare type T21 = MatchPair<'[foo,bar]'>;
declare type T22 = MatchPair<' [1,2]'>;
declare type T23 = MatchPair<'[123]'>;
declare type T24 = MatchPair<'[1,2,3,4]'>;
declare type SnakeToCamelCase<S extends string> = S extends `${infer T}_${infer U}` ? `${Lowercase<T>}${SnakeToPascalCase<U>}` : S extends `${infer T}` ? `${Lowercase<T>}` : SnakeToPascalCase<S>;
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;
declare type RR0 = SnakeToPascalCase<'hello_world_foo'>;
declare type RR1 = SnakeToPascalCase<'FOO_BAR_BAZ'>;
declare type RR2 = SnakeToCamelCase<'hello_world_foo'>;
declare type RR3 = SnakeToCamelCase<'FOO_BAR_BAZ'>;
declare type FirstTwoAndRest<S extends string> = S extends `${infer A}${infer B}${infer R}` ? [`${A}${B}`, R] : unknown;
declare type T25 = FirstTwoAndRest<'abcde'>;
declare type T26 = FirstTwoAndRest<'ab'>;
declare type T27 = FirstTwoAndRest<'a'>;
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';
declare type HexColor<S extends string> = S extends `#${infer R1}${infer R2}${infer G1}${infer G2}${infer B1}${infer B2}` ? [
R1,
R2,
G1,
G2,
B1,
B2
] extends [HexDigit, HexDigit, HexDigit, HexDigit, HexDigit, HexDigit] ? S : never : never;
declare type TH1 = HexColor<'#8080FF'>;
declare type TH2 = HexColor<'#80c0ff'>;
declare type TH3 = HexColor<'#8080F'>;
declare type TH4 = HexColor<'#8080FFF'>;
declare type Trim<S extends string> = S extends ` ${infer T}` ? Trim<T> : S extends `${infer T} ` ? Trim<T> : S;
declare type TR1 = Trim<'xx '>;
declare type TR2 = Trim<' xx'>;
declare type TR3 = Trim<' xx '>;
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>] : [
S
];
declare type T40 = Split<'foo', '.'>;
declare type T41 = Split<'foo.bar.baz', '.'>;
declare type T42 = Split<'foo.bar', ''>;
declare type T43 = Split<any, '.'>;
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];
declare function getProp<T, P0 extends keyof T & string, P1 extends keyof T[P0] & string>(obj: T, path: `${P0}.${P1}`): T[P0][P1];
declare function getProp<T, P0 extends keyof T & string>(obj: T, path: P0): T[P0];
declare function getProp(obj: object, path: string): unknown;
declare let p1: {
readonly b: {
readonly c: 42;
readonly d: "hello";
};
};
declare let p2: {
readonly c: 42;
readonly d: "hello";
};
declare let p3: "hello";
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;
declare function getPropValue<T, P extends string>(obj: T, path: P): PropType<T, P>;
declare const s: string;
declare const obj: {
a: {
b: {
c: number;
d: string;
};
};
};
declare type S1<T> = T extends `foo${infer U}bar` ? S2<U> : never;
declare type S2<S extends string> = S;
declare type TV1 = `${infer X}`;
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;
declare type L1 = Chars<'FooBarBazThisIsALongerString'>;
declare type Foo<T> = T extends `*${infer S}*` ? S : never;
declare type TF1 = Foo<any>;
declare type TF2 = Foo<string>;
declare type TF3 = Foo<'abc'>;
declare type TF4 = Foo<'*abc*'>;
declare type A = any;
declare type U1 = {
a1: A;
} | {
b1: A;
} | {
c1: A;
} | {
d1: A;
} | {
e1: A;
} | {
f1: A;
} | {
g1: A;
} | {
h1: A;
} | {
i1: A;
} | {
j1: A;
};
declare type U2 = {
a2: A;
} | {
b2: A;
} | {
c2: A;
} | {
d2: A;
} | {
e2: A;
} | {
f2: A;
} | {
g2: A;
} | {
h2: A;
} | {
i2: A;
} | {
j2: A;
};
declare type U3 = {
a3: A;
} | {
b3: A;
} | {
c3: A;
} | {
d3: A;
} | {
e3: A;
} | {
f3: A;
} | {
g3: A;
} | {
h3: A;
} | {
i3: A;
} | {
j3: A;
};
declare type U4 = {
a4: A;
} | {
b4: A;
} | {
c4: A;
} | {
d4: A;
} | {
e4: A;
} | {
f4: A;
} | {
g4: A;
} | {
h4: A;
} | {
i4: A;
} | {
j4: A;
};
declare type U5 = {
a5: A;
} | {
b5: A;
} | {
c5: A;
} | {
d5: A;
} | {
e5: A;
} | {
f5: A;
} | {
g5: A;
} | {
h5: A;
} | {
i5: A;
} | {
j5: A;
};
declare type U100000 = U1 & U2 & U3 & U4 & U5;
declare type Digits = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
declare type D100000 = `${Digits}${Digits}${Digits}${Digits}${Digits}`;
declare type TDigits = [0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9];
declare type T100000 = [...TDigits, ...TDigits, ...TDigits, ...TDigits, ...TDigits];
declare type IsNegative<T extends number> = `${T}` extends `-${string}` ? true : false;
declare type AA<T extends number, Q extends number> = [
true,
true
] extends [IsNegative<T>, IsNegative<Q>] ? 'Every thing is ok!' : ['strange', IsNegative<T>, IsNegative<Q>];
declare type BB = AA<-2, -2>;
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;
declare type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
declare const obj2: {
readonly name: "John";
readonly age: 42;
readonly cars: readonly [{
readonly make: "Ford";
readonly age: 10;
}, {
readonly make: "Trabant";
readonly age: 35;
}];
};
declare let make: "Trabant";
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}`;
export declare type SpacingShorthand = `${Spacing} ${Spacing}` | `${Spacing} ${Spacing} ${Spacing}` | `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;

View file

@ -952,3 +952,39 @@ let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
>getProp2 : Symbol(getProp2, Decl(templateLiteralTypes1.ts, 222, 89))
>obj2 : Symbol(obj2, Decl(templateLiteralTypes1.ts, 226, 5))
// Repro from #46480
export type Spacing =
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
| `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}`;
const spacing: Spacing = "s12"
>spacing : Symbol(spacing, Decl(templateLiteralTypes1.ts, 245, 5))
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
export type SpacingShorthand =
>SpacingShorthand : Symbol(SpacingShorthand, Decl(templateLiteralTypes1.ts, 245, 30))
| `${Spacing} ${Spacing}`
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
| `${Spacing} ${Spacing} ${Spacing}`
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
>Spacing : Symbol(Spacing, Decl(templateLiteralTypes1.ts, 235, 41))
const test1: SpacingShorthand = "0 0 0";
>test1 : Symbol(test1, Decl(templateLiteralTypes1.ts, 252, 5))
>SpacingShorthand : Symbol(SpacingShorthand, Decl(templateLiteralTypes1.ts, 245, 30))

View file

@ -594,3 +594,28 @@ let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
>obj2 : { readonly name: "John"; readonly age: 42; readonly cars: readonly [{ readonly make: "Ford"; readonly age: 10; }, { readonly make: "Trabant"; readonly age: 35; }]; }
>'cars.1.make' : "cars.1.make"
// Repro from #46480
export type Spacing =
>Spacing : 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}`;
const spacing: Spacing = "s12"
>spacing : Spacing
>"s12" : "s12"
export type SpacingShorthand =
>SpacingShorthand : any
| `${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
const test1: SpacingShorthand = "0 0 0";
>test1 : any
>"0 0 0" : "0 0 0"

View file

@ -237,3 +237,20 @@ const obj2 = {
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
// Repro from #46480
export 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}`;
const spacing: Spacing = "s12"
export type SpacingShorthand =
| `${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing}`
| `${Spacing} ${Spacing} ${Spacing} ${Spacing}`;
const test1: SpacingShorthand = "0 0 0";