Skip to content

Commit

Permalink
Add inference priority for mapped type keys (#22246)
Browse files Browse the repository at this point in the history
* Add inference priority for mapped type keys

The new priority causes union inference, similarly to return type

* Rename priority

* Fix comment typo
  • Loading branch information
weswigham authored Mar 5, 2018
1 parent 2ac2291 commit 53ae507
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 15 deletions.
7 changes: 5 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11789,7 +11789,7 @@ namespace ts {
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
if (inferredType) {
const savePriority = priority;
priority |= InferencePriority.MappedType;
priority |= InferencePriority.HomomorphicMappedType;
inferFromTypes(inferredType, inference.typeParameter);
priority = savePriority;
}
Expand All @@ -11799,7 +11799,10 @@ namespace ts {
if (constraintType.flags & TypeFlags.TypeParameter) {
// We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type
// parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X.
const savePriority = priority;
priority |= InferencePriority.MappedTypeConstraint;
inferFromTypes(getIndexType(source), constraintType);
priority = savePriority;
inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
return;
}
Expand Down Expand Up @@ -11932,7 +11935,7 @@ namespace ts {
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
// union types were requested or if all inferences were made from the return type position, infer a
// union type. Otherwise, infer a common supertype.
const unwidenedType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ?
const unwidenedType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.PriorityImpliesUnion ?
getUnionType(baseCandidates, UnionReduction.Subtype) :
getCommonSupertype(baseCandidates);
inferredType = getWidenedType(unwidenedType);
Expand Down
13 changes: 8 additions & 5 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3919,11 +3919,14 @@ namespace ts {
export type TypeMapper = (t: TypeParameter) => Type;

export const enum InferencePriority {
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
MappedType = 1 << 1, // Reverse inference for mapped type
ReturnType = 1 << 2, // Inference made from return type of generic function
NoConstraints = 1 << 3, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 4, // Always use strict rules for contravariant inferences
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
MappedTypeConstraint = 1 << 2, // Reverse inference for mapped type
ReturnType = 1 << 3, // Inference made from return type of generic function
NoConstraints = 1 << 4, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 5, // Always use strict rules for contravariant inferences

PriorityImpliesUnion = ReturnType | MappedTypeConstraint, // These priorities imply that the resulting type should be a union of all candidates
}

/* @internal */
Expand Down
10 changes: 6 additions & 4 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2236,10 +2236,12 @@ declare namespace ts {
}
enum InferencePriority {
NakedTypeVariable = 1,
MappedType = 2,
ReturnType = 4,
NoConstraints = 8,
AlwaysStrict = 16,
HomomorphicMappedType = 2,
MappedTypeConstraint = 4,
ReturnType = 8,
NoConstraints = 16,
AlwaysStrict = 32,
PriorityImpliesUnion = 12,
}
interface JsFileExtensionInfo {
extension: string;
Expand Down
10 changes: 6 additions & 4 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2236,10 +2236,12 @@ declare namespace ts {
}
enum InferencePriority {
NakedTypeVariable = 1,
MappedType = 2,
ReturnType = 4,
NoConstraints = 8,
AlwaysStrict = 16,
HomomorphicMappedType = 2,
MappedTypeConstraint = 4,
ReturnType = 8,
NoConstraints = 16,
AlwaysStrict = 32,
PriorityImpliesUnion = 12,
}
interface JsFileExtensionInfo {
extension: string;
Expand Down
34 changes: 34 additions & 0 deletions tests/baselines/reference/mappedTypeMultiInference.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//// [mappedTypeMultiInference.ts]
interface Style {
flashy: any;
}

declare function mergeStyleSets<K extends string>(
...cssSets: { [P in K]?: Style }[]): { [P in K]: Style };

// Expected:
// let x: {
// a: Style;
// b: Style;
// }
let x = mergeStyleSets(
{},
{
a: { flashy: true },
},
{
b: { flashy: true },
},
)

//// [mappedTypeMultiInference.js]
// Expected:
// let x: {
// a: Style;
// b: Style;
// }
var x = mergeStyleSets({}, {
a: { flashy: true }
}, {
b: { flashy: true }
});
44 changes: 44 additions & 0 deletions tests/baselines/reference/mappedTypeMultiInference.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
=== tests/cases/compiler/mappedTypeMultiInference.ts ===
interface Style {
>Style : Symbol(Style, Decl(mappedTypeMultiInference.ts, 0, 0))

flashy: any;
>flashy : Symbol(Style.flashy, Decl(mappedTypeMultiInference.ts, 0, 17))
}

declare function mergeStyleSets<K extends string>(
>mergeStyleSets : Symbol(mergeStyleSets, Decl(mappedTypeMultiInference.ts, 2, 1))
>K : Symbol(K, Decl(mappedTypeMultiInference.ts, 4, 32))

...cssSets: { [P in K]?: Style }[]): { [P in K]: Style };
>cssSets : Symbol(cssSets, Decl(mappedTypeMultiInference.ts, 4, 50))
>P : Symbol(P, Decl(mappedTypeMultiInference.ts, 5, 19))
>K : Symbol(K, Decl(mappedTypeMultiInference.ts, 4, 32))
>Style : Symbol(Style, Decl(mappedTypeMultiInference.ts, 0, 0))
>P : Symbol(P, Decl(mappedTypeMultiInference.ts, 5, 44))
>K : Symbol(K, Decl(mappedTypeMultiInference.ts, 4, 32))
>Style : Symbol(Style, Decl(mappedTypeMultiInference.ts, 0, 0))

// Expected:
// let x: {
// a: Style;
// b: Style;
// }
let x = mergeStyleSets(
>x : Symbol(x, Decl(mappedTypeMultiInference.ts, 12, 3))
>mergeStyleSets : Symbol(mergeStyleSets, Decl(mappedTypeMultiInference.ts, 2, 1))

{},
{
a: { flashy: true },
>a : Symbol(a, Decl(mappedTypeMultiInference.ts, 14, 5))
>flashy : Symbol(flashy, Decl(mappedTypeMultiInference.ts, 15, 12))

},
{
b: { flashy: true },
>b : Symbol(b, Decl(mappedTypeMultiInference.ts, 17, 5))
>flashy : Symbol(flashy, Decl(mappedTypeMultiInference.ts, 18, 12))

},
)
54 changes: 54 additions & 0 deletions tests/baselines/reference/mappedTypeMultiInference.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
=== tests/cases/compiler/mappedTypeMultiInference.ts ===
interface Style {
>Style : Style

flashy: any;
>flashy : any
}

declare function mergeStyleSets<K extends string>(
>mergeStyleSets : <K extends string>(...cssSets: { [P in K]?: Style; }[]) => { [P in K]: Style; }
>K : K

...cssSets: { [P in K]?: Style }[]): { [P in K]: Style };
>cssSets : { [P in K]?: Style; }[]
>P : P
>K : K
>Style : Style
>P : P
>K : K
>Style : Style

// Expected:
// let x: {
// a: Style;
// b: Style;
// }
let x = mergeStyleSets(
>x : { a: Style; b: Style; }
>mergeStyleSets( {}, { a: { flashy: true }, }, { b: { flashy: true }, },) : { a: Style; b: Style; }
>mergeStyleSets : <K extends string>(...cssSets: { [P in K]?: Style; }[]) => { [P in K]: Style; }

{},
>{} : {}
{
>{ a: { flashy: true }, } : { a: { flashy: boolean; }; }

a: { flashy: true },
>a : { flashy: boolean; }
>{ flashy: true } : { flashy: boolean; }
>flashy : boolean
>true : true

},
{
>{ b: { flashy: true }, } : { b: { flashy: boolean; }; }

b: { flashy: true },
>b : { flashy: boolean; }
>{ flashy: true } : { flashy: boolean; }
>flashy : boolean
>true : true

},
)
21 changes: 21 additions & 0 deletions tests/cases/compiler/mappedTypeMultiInference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
interface Style {
flashy: any;
}

declare function mergeStyleSets<K extends string>(
...cssSets: { [P in K]?: Style }[]): { [P in K]: Style };

// Expected:
// let x: {
// a: Style;
// b: Style;
// }
let x = mergeStyleSets(
{},
{
a: { flashy: true },
},
{
b: { flashy: true },
},
)

0 comments on commit 53ae507

Please sign in to comment.