diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 20613d966d9..b3a590f3e90 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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) { @@ -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 @@ -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)); + } } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f72207f24dc..fbe0d3c17a4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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[]; } diff --git a/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.js b/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.js new file mode 100644 index 00000000000..6be0f4361db --- /dev/null +++ b/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.js @@ -0,0 +1,7 @@ +//// [objectBindingPatternContextuallyTypesArgument.ts] +declare function id(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; diff --git a/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.symbols b/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.symbols new file mode 100644 index 00000000000..77e2e0ad7d4 --- /dev/null +++ b/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/objectBindingPatternContextuallyTypesArgument.ts === +declare function id(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, --, --)) + diff --git a/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.types b/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.types new file mode 100644 index 00000000000..b5c4974a079 --- /dev/null +++ b/tests/baselines/reference/objectBindingPatternContextuallyTypesArgument.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/objectBindingPatternContextuallyTypesArgument.ts === +declare function id(x: T): T; +>id : (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 : (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 + diff --git a/tests/cases/compiler/objectBindingPatternContextuallyTypesArgument.ts b/tests/cases/compiler/objectBindingPatternContextuallyTypesArgument.ts new file mode 100644 index 00000000000..dc79200ae6e --- /dev/null +++ b/tests/cases/compiler/objectBindingPatternContextuallyTypesArgument.ts @@ -0,0 +1,2 @@ +declare function id(x: T): T; +const { f = (x: string) => x.length } = id({ f: x => x.charAt });