Skip to content

Commit 53ae507

Browse files
authored
Add inference priority for mapped type keys (#22246)
* Add inference priority for mapped type keys The new priority causes union inference, similarly to return type * Rename priority * Fix comment typo
1 parent 2ac2291 commit 53ae507

8 files changed

+178
-15
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11789,7 +11789,7 @@ namespace ts {
1178911789
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
1179011790
if (inferredType) {
1179111791
const savePriority = priority;
11792-
priority |= InferencePriority.MappedType;
11792+
priority |= InferencePriority.HomomorphicMappedType;
1179311793
inferFromTypes(inferredType, inference.typeParameter);
1179411794
priority = savePriority;
1179511795
}
@@ -11799,7 +11799,10 @@ namespace ts {
1179911799
if (constraintType.flags & TypeFlags.TypeParameter) {
1180011800
// We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type
1180111801
// parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X.
11802+
const savePriority = priority;
11803+
priority |= InferencePriority.MappedTypeConstraint;
1180211804
inferFromTypes(getIndexType(source), constraintType);
11805+
priority = savePriority;
1180311806
inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
1180411807
return;
1180511808
}
@@ -11932,7 +11935,7 @@ namespace ts {
1193211935
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
1193311936
// union types were requested or if all inferences were made from the return type position, infer a
1193411937
// union type. Otherwise, infer a common supertype.
11935-
const unwidenedType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ?
11938+
const unwidenedType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.PriorityImpliesUnion ?
1193611939
getUnionType(baseCandidates, UnionReduction.Subtype) :
1193711940
getCommonSupertype(baseCandidates);
1193811941
inferredType = getWidenedType(unwidenedType);

src/compiler/types.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3919,11 +3919,14 @@ namespace ts {
39193919
export type TypeMapper = (t: TypeParameter) => Type;
39203920

39213921
export const enum InferencePriority {
3922-
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
3923-
MappedType = 1 << 1, // Reverse inference for mapped type
3924-
ReturnType = 1 << 2, // Inference made from return type of generic function
3925-
NoConstraints = 1 << 3, // Don't infer from constraints of instantiable types
3926-
AlwaysStrict = 1 << 4, // Always use strict rules for contravariant inferences
3922+
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
3923+
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
3924+
MappedTypeConstraint = 1 << 2, // Reverse inference for mapped type
3925+
ReturnType = 1 << 3, // Inference made from return type of generic function
3926+
NoConstraints = 1 << 4, // Don't infer from constraints of instantiable types
3927+
AlwaysStrict = 1 << 5, // Always use strict rules for contravariant inferences
3928+
3929+
PriorityImpliesUnion = ReturnType | MappedTypeConstraint, // These priorities imply that the resulting type should be a union of all candidates
39273930
}
39283931

39293932
/* @internal */

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,10 +2236,12 @@ declare namespace ts {
22362236
}
22372237
enum InferencePriority {
22382238
NakedTypeVariable = 1,
2239-
MappedType = 2,
2240-
ReturnType = 4,
2241-
NoConstraints = 8,
2242-
AlwaysStrict = 16,
2239+
HomomorphicMappedType = 2,
2240+
MappedTypeConstraint = 4,
2241+
ReturnType = 8,
2242+
NoConstraints = 16,
2243+
AlwaysStrict = 32,
2244+
PriorityImpliesUnion = 12,
22432245
}
22442246
interface JsFileExtensionInfo {
22452247
extension: string;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,10 +2236,12 @@ declare namespace ts {
22362236
}
22372237
enum InferencePriority {
22382238
NakedTypeVariable = 1,
2239-
MappedType = 2,
2240-
ReturnType = 4,
2241-
NoConstraints = 8,
2242-
AlwaysStrict = 16,
2239+
HomomorphicMappedType = 2,
2240+
MappedTypeConstraint = 4,
2241+
ReturnType = 8,
2242+
NoConstraints = 16,
2243+
AlwaysStrict = 32,
2244+
PriorityImpliesUnion = 12,
22432245
}
22442246
interface JsFileExtensionInfo {
22452247
extension: string;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [mappedTypeMultiInference.ts]
2+
interface Style {
3+
flashy: any;
4+
}
5+
6+
declare function mergeStyleSets<K extends string>(
7+
...cssSets: { [P in K]?: Style }[]): { [P in K]: Style };
8+
9+
// Expected:
10+
// let x: {
11+
// a: Style;
12+
// b: Style;
13+
// }
14+
let x = mergeStyleSets(
15+
{},
16+
{
17+
a: { flashy: true },
18+
},
19+
{
20+
b: { flashy: true },
21+
},
22+
)
23+
24+
//// [mappedTypeMultiInference.js]
25+
// Expected:
26+
// let x: {
27+
// a: Style;
28+
// b: Style;
29+
// }
30+
var x = mergeStyleSets({}, {
31+
a: { flashy: true }
32+
}, {
33+
b: { flashy: true }
34+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/compiler/mappedTypeMultiInference.ts ===
2+
interface Style {
3+
>Style : Symbol(Style, Decl(mappedTypeMultiInference.ts, 0, 0))
4+
5+
flashy: any;
6+
>flashy : Symbol(Style.flashy, Decl(mappedTypeMultiInference.ts, 0, 17))
7+
}
8+
9+
declare function mergeStyleSets<K extends string>(
10+
>mergeStyleSets : Symbol(mergeStyleSets, Decl(mappedTypeMultiInference.ts, 2, 1))
11+
>K : Symbol(K, Decl(mappedTypeMultiInference.ts, 4, 32))
12+
13+
...cssSets: { [P in K]?: Style }[]): { [P in K]: Style };
14+
>cssSets : Symbol(cssSets, Decl(mappedTypeMultiInference.ts, 4, 50))
15+
>P : Symbol(P, Decl(mappedTypeMultiInference.ts, 5, 19))
16+
>K : Symbol(K, Decl(mappedTypeMultiInference.ts, 4, 32))
17+
>Style : Symbol(Style, Decl(mappedTypeMultiInference.ts, 0, 0))
18+
>P : Symbol(P, Decl(mappedTypeMultiInference.ts, 5, 44))
19+
>K : Symbol(K, Decl(mappedTypeMultiInference.ts, 4, 32))
20+
>Style : Symbol(Style, Decl(mappedTypeMultiInference.ts, 0, 0))
21+
22+
// Expected:
23+
// let x: {
24+
// a: Style;
25+
// b: Style;
26+
// }
27+
let x = mergeStyleSets(
28+
>x : Symbol(x, Decl(mappedTypeMultiInference.ts, 12, 3))
29+
>mergeStyleSets : Symbol(mergeStyleSets, Decl(mappedTypeMultiInference.ts, 2, 1))
30+
31+
{},
32+
{
33+
a: { flashy: true },
34+
>a : Symbol(a, Decl(mappedTypeMultiInference.ts, 14, 5))
35+
>flashy : Symbol(flashy, Decl(mappedTypeMultiInference.ts, 15, 12))
36+
37+
},
38+
{
39+
b: { flashy: true },
40+
>b : Symbol(b, Decl(mappedTypeMultiInference.ts, 17, 5))
41+
>flashy : Symbol(flashy, Decl(mappedTypeMultiInference.ts, 18, 12))
42+
43+
},
44+
)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
=== tests/cases/compiler/mappedTypeMultiInference.ts ===
2+
interface Style {
3+
>Style : Style
4+
5+
flashy: any;
6+
>flashy : any
7+
}
8+
9+
declare function mergeStyleSets<K extends string>(
10+
>mergeStyleSets : <K extends string>(...cssSets: { [P in K]?: Style; }[]) => { [P in K]: Style; }
11+
>K : K
12+
13+
...cssSets: { [P in K]?: Style }[]): { [P in K]: Style };
14+
>cssSets : { [P in K]?: Style; }[]
15+
>P : P
16+
>K : K
17+
>Style : Style
18+
>P : P
19+
>K : K
20+
>Style : Style
21+
22+
// Expected:
23+
// let x: {
24+
// a: Style;
25+
// b: Style;
26+
// }
27+
let x = mergeStyleSets(
28+
>x : { a: Style; b: Style; }
29+
>mergeStyleSets( {}, { a: { flashy: true }, }, { b: { flashy: true }, },) : { a: Style; b: Style; }
30+
>mergeStyleSets : <K extends string>(...cssSets: { [P in K]?: Style; }[]) => { [P in K]: Style; }
31+
32+
{},
33+
>{} : {}
34+
{
35+
>{ a: { flashy: true }, } : { a: { flashy: boolean; }; }
36+
37+
a: { flashy: true },
38+
>a : { flashy: boolean; }
39+
>{ flashy: true } : { flashy: boolean; }
40+
>flashy : boolean
41+
>true : true
42+
43+
},
44+
{
45+
>{ b: { flashy: true }, } : { b: { flashy: boolean; }; }
46+
47+
b: { flashy: true },
48+
>b : { flashy: boolean; }
49+
>{ flashy: true } : { flashy: boolean; }
50+
>flashy : boolean
51+
>true : true
52+
53+
},
54+
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
interface Style {
2+
flashy: any;
3+
}
4+
5+
declare function mergeStyleSets<K extends string>(
6+
...cssSets: { [P in K]?: Style }[]): { [P in K]: Style };
7+
8+
// Expected:
9+
// let x: {
10+
// a: Style;
11+
// b: Style;
12+
// }
13+
let x = mergeStyleSets(
14+
{},
15+
{
16+
a: { flashy: true },
17+
},
18+
{
19+
b: { flashy: true },
20+
},
21+
)

0 commit comments

Comments
 (0)