From 31d0e8b522cce93d2ebd47c7cae9c6b483530fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 3 Jan 2024 18:13:39 +0100 Subject: [PATCH 1/2] Prefer higher-priority inferences from generic signatures over low-priority inferences from the first pass --- src/compiler/checker.ts | 17 +++-- .../genericFunctionInference3.symbols | 60 ++++++++++++++++++ .../reference/genericFunctionInference3.types | 62 +++++++++++++++++++ .../compiler/genericFunctionInference3.ts | 26 ++++++++ 4 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/genericFunctionInference3.symbols create mode 100644 tests/baselines/reference/genericFunctionInference3.types create mode 100644 tests/cases/compiler/genericFunctionInference3.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d4f288f1293b..a808174914010 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -38973,7 +38973,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // potentially add inferred type parameters to the outer function return type. const returnType = context.signature && getReturnTypeOfSignature(context.signature); const returnSignature = returnType && getSingleCallOrConstructSignature(returnType); - if (returnSignature && !returnSignature.typeParameters && !every(context.inferences, hasInferenceCandidates)) { + if (returnSignature && !returnSignature.typeParameters && !every(context.inferences, info => hasInferenceCandidates(info) && info.priority === InferencePriority.None)) { // Instantiate the signature with its own type parameters as type arguments, possibly // renaming the type parameters to ensure they have unique names. const uniqueTypeParameters = getUniqueTypeParameters(context, signature.typeParameters); @@ -38993,8 +38993,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If the type parameters for which we produced candidates do not have any inferences yet, // we adopt the new inference candidates and add the type parameters of the expression type // to the set of inferred type parameters for the outer function return type. - if (!hasOverlappingInferences(context.inferences, inferences)) { - mergeInferences(context.inferences, inferences); + const merged = tryInferencesMerge(context.inferences, inferences); + if (merged) { context.inferredTypeParameters = concatenate(context.inferredTypeParameters, uniqueTypeParameters); return getOrCreateTypeFromSignature(instantiatedSignature); } @@ -39034,12 +39034,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } - function mergeInferences(target: InferenceInfo[], source: InferenceInfo[]) { + function tryInferencesMerge(target: InferenceInfo[], source: InferenceInfo[]) { + let merged = false; + + const areOverlapping = hasOverlappingInferences(target, source); + for (let i = 0; i < target.length; i++) { - if (!hasInferenceCandidates(target[i]) && hasInferenceCandidates(source[i])) { + if (areOverlapping ? hasInferenceCandidates(source[i]) && hasInferenceCandidates(target[i]) && source[i].priority! < (target[i].priority! & ~InferencePriority.ReturnType) : hasInferenceCandidates(source[i])) { target[i] = source[i]; + target[i].priority = InferencePriority.None; + merged = true; } } + return merged; } function getUniqueTypeParameters(context: InferenceContext, typeParameters: readonly TypeParameter[]): readonly TypeParameter[] { diff --git a/tests/baselines/reference/genericFunctionInference3.symbols b/tests/baselines/reference/genericFunctionInference3.symbols new file mode 100644 index 0000000000000..114a5ff578fd0 --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference3.symbols @@ -0,0 +1,60 @@ +//// [tests/cases/compiler/genericFunctionInference3.ts] //// + +=== genericFunctionInference3.ts === +// https://github.com/microsoft/TypeScript/issues/56931 + +declare function defineComponent>( +>defineComponent : Symbol(defineComponent, Decl(genericFunctionInference3.ts, 0, 0)) +>Props : Symbol(Props, Decl(genericFunctionInference3.ts, 2, 33)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + + setup: (props: Props) => void, +>setup : Symbol(setup, Decl(genericFunctionInference3.ts, 2, 68)) +>props : Symbol(props, Decl(genericFunctionInference3.ts, 3, 10)) +>Props : Symbol(Props, Decl(genericFunctionInference3.ts, 2, 33)) + + options?: { +>options : Symbol(options, Decl(genericFunctionInference3.ts, 3, 32)) + + props?: (keyof Props)[]; +>props : Symbol(props, Decl(genericFunctionInference3.ts, 4, 13)) +>Props : Symbol(Props, Decl(genericFunctionInference3.ts, 2, 33)) + + }, +): (props: Props) => unknown; +>props : Symbol(props, Decl(genericFunctionInference3.ts, 7, 4)) +>Props : Symbol(Props, Decl(genericFunctionInference3.ts, 2, 33)) + +const res1 = defineComponent( +>res1 : Symbol(res1, Decl(genericFunctionInference3.ts, 9, 5)) +>defineComponent : Symbol(defineComponent, Decl(genericFunctionInference3.ts, 0, 0)) + + (_props: { msg: T }) => { +>T : Symbol(T, Decl(genericFunctionInference3.ts, 10, 3)) +>_props : Symbol(_props, Decl(genericFunctionInference3.ts, 10, 21)) +>msg : Symbol(msg, Decl(genericFunctionInference3.ts, 10, 30)) +>T : Symbol(T, Decl(genericFunctionInference3.ts, 10, 3)) + + return () => {}; + } +); + +const res2 = defineComponent( +>res2 : Symbol(res2, Decl(genericFunctionInference3.ts, 15, 5)) +>defineComponent : Symbol(defineComponent, Decl(genericFunctionInference3.ts, 0, 0)) + + (_props: { msg: T }) => { +>T : Symbol(T, Decl(genericFunctionInference3.ts, 16, 3)) +>_props : Symbol(_props, Decl(genericFunctionInference3.ts, 16, 21)) +>msg : Symbol(msg, Decl(genericFunctionInference3.ts, 16, 30)) +>T : Symbol(T, Decl(genericFunctionInference3.ts, 16, 3)) + + return () => {}; + }, + { + props: ["msg"], +>props : Symbol(props, Decl(genericFunctionInference3.ts, 19, 3)) + + }, +); + diff --git a/tests/baselines/reference/genericFunctionInference3.types b/tests/baselines/reference/genericFunctionInference3.types new file mode 100644 index 0000000000000..697cb03213884 --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference3.types @@ -0,0 +1,62 @@ +//// [tests/cases/compiler/genericFunctionInference3.ts] //// + +=== genericFunctionInference3.ts === +// https://github.com/microsoft/TypeScript/issues/56931 + +declare function defineComponent>( +>defineComponent : >(setup: (props: Props) => void, options?: { props?: (keyof Props)[];}) => (props: Props) => unknown + + setup: (props: Props) => void, +>setup : (props: Props) => void +>props : Props + + options?: { +>options : { props?: (keyof Props)[] | undefined; } | undefined + + props?: (keyof Props)[]; +>props : (keyof Props)[] | undefined + + }, +): (props: Props) => unknown; +>props : Props + +const res1 = defineComponent( +>res1 : (props: { msg: T; }) => unknown +>defineComponent( (_props: { msg: T }) => { return () => {}; }) : (props: { msg: T; }) => unknown +>defineComponent : >(setup: (props: Props) => void, options?: { props?: (keyof Props)[] | undefined; } | undefined) => (props: Props) => unknown + + (_props: { msg: T }) => { +>(_props: { msg: T }) => { return () => {}; } : (_props: { msg: T;}) => () => void +>_props : { msg: T; } +>msg : T + + return () => {}; +>() => {} : () => void + } +); + +const res2 = defineComponent( +>res2 : (props: { msg: T; }) => unknown +>defineComponent( (_props: { msg: T }) => { return () => {}; }, { props: ["msg"], },) : (props: { msg: T; }) => unknown +>defineComponent : >(setup: (props: Props) => void, options?: { props?: (keyof Props)[] | undefined; } | undefined) => (props: Props) => unknown + + (_props: { msg: T }) => { +>(_props: { msg: T }) => { return () => {}; } : (_props: { msg: T;}) => () => void +>_props : { msg: T; } +>msg : T + + return () => {}; +>() => {} : () => void + + }, + { +>{ props: ["msg"], } : { props: "msg"[]; } + + props: ["msg"], +>props : "msg"[] +>["msg"] : "msg"[] +>"msg" : "msg" + + }, +); + diff --git a/tests/cases/compiler/genericFunctionInference3.ts b/tests/cases/compiler/genericFunctionInference3.ts new file mode 100644 index 0000000000000..63d441e5e6627 --- /dev/null +++ b/tests/cases/compiler/genericFunctionInference3.ts @@ -0,0 +1,26 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/56931 + +declare function defineComponent>( + setup: (props: Props) => void, + options?: { + props?: (keyof Props)[]; + }, +): (props: Props) => unknown; + +const res1 = defineComponent( + (_props: { msg: T }) => { + return () => {}; + } +); + +const res2 = defineComponent( + (_props: { msg: T }) => { + return () => {}; + }, + { + props: ["msg"], + }, +); From b89cb877665cfb41d6f5af94d7d6fd924efe1d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 10 Jan 2024 13:06:59 +0100 Subject: [PATCH 2/2] clone inference info --- src/compiler/checker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a808174914010..0599d2fda8925 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39041,8 +39041,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { for (let i = 0; i < target.length; i++) { if (areOverlapping ? hasInferenceCandidates(source[i]) && hasInferenceCandidates(target[i]) && source[i].priority! < (target[i].priority! & ~InferencePriority.ReturnType) : hasInferenceCandidates(source[i])) { - target[i] = source[i]; - target[i].priority = InferencePriority.None; + const clonedInferenceInfo = cloneInferenceInfo(source[i]); + clonedInferenceInfo.priority = InferencePriority.None; + target[i] = clonedInferenceInfo; merged = true; } }