Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inference priority for mapped type keys #22246

Merged
merged 4 commits into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 },
},
)