Skip to content

Commit

Permalink
Fix type mapper combination
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewbranch committed May 13, 2022
1 parent 7e8c56c commit 7dc1952
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 5 deletions.
26 changes: 21 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21840,6 +21840,20 @@ namespace ts {
return t;
}

function mapToInferredTypeIncludingReturnTypeInferences(context: InferenceContext, returnContext: InferenceContext, t: Type): Type {
const inferences = context.inferences;
for (let i = 0; i < inferences.length; i++) {
const inference = inferences[i];
if (t === inference.typeParameter) {
if (inference.inferredType || hasInferenceCandidates(inference)) {
return getInferredType(context, i);
}
return getMappedType(t, returnContext.mapper);
}
}
return t;
}

function clearCachedInferences(inferences: InferenceInfo[]) {
for (const inference of inferences) {
if (!inference.isFixed) {
Expand Down Expand Up @@ -27055,12 +27069,10 @@ namespace ts {
const inferenceContext = getInferenceContext(node);
// If no inferences have been made, nothing is gained from instantiating as type parameters
// would just be replaced with their defaults similar to the apparent type.
if (inferenceContext && contextFlags! & ContextFlags.Signature && (inferenceContext.returnMapper || some(inferenceContext.inferences, hasInferenceCandidates))) {
if (inferenceContext && contextFlags! & ContextFlags.Signature && (inferenceContext.combinedReturnMapper || some(inferenceContext.inferences, hasInferenceCandidates))) {
// For contextual signatures we incorporate all inferences made so far, e.g. from return
// types as well as arguments to the left in a function call.
return instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper
? combineTypeMappers(inferenceContext.nonFixingMapper, inferenceContext.returnMapper)
: inferenceContext.nonFixingMapper);
return instantiateInstantiableTypes(contextualType, inferenceContext.combinedReturnMapper || inferenceContext.nonFixingMapper);
}
if (inferenceContext?.returnMapper) {
// For other purposes (e.g. determining whether to produce literal types) we only
Expand Down Expand Up @@ -29950,7 +29962,11 @@ namespace ts {
const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags);
const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper);
inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType);
context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined;
if (some(returnContext.inferences, hasInferenceCandidates)) {
const clonedReturnContext = cloneInferredPartOfContext(returnContext)!;
context.returnMapper = getMapperFromContext(clonedReturnContext);
context.combinedReturnMapper = makeFunctionTypeMapper(t => mapToInferredTypeIncludingReturnTypeInferences(context, clonedReturnContext, t));
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5922,6 +5922,7 @@ namespace ts {
mapper: TypeMapper; // Mapper that fixes inferences
nonFixingMapper: TypeMapper; // Mapper that doesn't fix inferences
returnMapper?: TypeMapper; // Type mapper for inferences from return types (if any)
combinedReturnMapper?: TypeMapper; // Non-fixing mapper combined with return mapper for contextual signature instantiation
inferredTypeParameters?: readonly TypeParameter[]; // Inferred type parameters for function result
intraExpressionInferenceSites?: IntraExpressionInferenceSite[];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//// [objectBindingPatternContextuallyTypesArgument.ts]
declare function id<T>(x: T): T;
const { f = (x: string) => x.length } = id({ f: x => x.charAt });


//// [objectBindingPatternContextuallyTypesArgument.js]
var _a = id({ f: function (x) { return x.charAt; } }).f, f = _a === void 0 ? function (x) { return x.length; } : _a;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
=== tests/cases/compiler/objectBindingPatternContextuallyTypesArgument.ts ===
declare function id<T>(x: T): T;
>id : Symbol(id, Decl(objectBindingPatternContextuallyTypesArgument.ts, 0, 0))
>T : Symbol(T, Decl(objectBindingPatternContextuallyTypesArgument.ts, 0, 20))
>x : Symbol(x, Decl(objectBindingPatternContextuallyTypesArgument.ts, 0, 23))
>T : Symbol(T, Decl(objectBindingPatternContextuallyTypesArgument.ts, 0, 20))
>T : Symbol(T, Decl(objectBindingPatternContextuallyTypesArgument.ts, 0, 20))

const { f = (x: string) => x.length } = id({ f: x => x.charAt });
>f : Symbol(f, Decl(objectBindingPatternContextuallyTypesArgument.ts, 1, 7))
>x : Symbol(x, Decl(objectBindingPatternContextuallyTypesArgument.ts, 1, 13))
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(objectBindingPatternContextuallyTypesArgument.ts, 1, 13))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>id : Symbol(id, Decl(objectBindingPatternContextuallyTypesArgument.ts, 0, 0))
>f : Symbol(f, Decl(objectBindingPatternContextuallyTypesArgument.ts, 1, 44))
>x : Symbol(x, Decl(objectBindingPatternContextuallyTypesArgument.ts, 1, 47))
>x.charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(objectBindingPatternContextuallyTypesArgument.ts, 1, 47))
>charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --))

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/compiler/objectBindingPatternContextuallyTypesArgument.ts ===
declare function id<T>(x: T): T;
>id : <T>(x: T) => T
>x : T

const { f = (x: string) => x.length } = id({ f: x => x.charAt });
>f : ((x: string) => number) | ((x: string) => (pos: number) => string)
>(x: string) => x.length : (x: string) => number
>x : string
>x.length : number
>x : string
>length : number
>id({ f: x => x.charAt }) : { f: (x: string) => (pos: number) => string; }
>id : <T>(x: T) => T
>{ f: x => x.charAt } : { f: (x: string) => (pos: number) => string; }
>f : (x: string) => (pos: number) => string
>x => x.charAt : (x: string) => (pos: number) => string
>x : string
>x.charAt : (pos: number) => string
>x : string
>charAt : (pos: number) => string

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare function id<T>(x: T): T;
const { f = (x: string) => x.length } = id({ f: x => x.charAt });

0 comments on commit 7dc1952

Please sign in to comment.