diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8f1bf66fa63da..0ee591294a8db 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33324,10 +33324,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return cached; } links.resolvedSignature = resolvingSignature; - const result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal); + let result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal); // When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call // resolution should be deferred. if (result !== resolvingSignature) { + // if the signature resolution originated on a node that itself depends on the contextual type + // then it's possible that the resolved signature might not be the same as the one that would be computed in source order + // since resolving such signature leads to resolving the potential outer signature, its arguments and thus the very same signature + // it's possible that this inner resolution sets the resolvedSignature first. + // In such a case we ignore the local result and reuse the correct one that was cached. + if (links.resolvedSignature !== resolvingSignature) { + result = links.resolvedSignature; + } // If signature resolution originated in control flow type analysis (for example to compute the // assigned type in a flow assignment) we don't cache the result as it may be based on temporary // types from the control flow analysis. diff --git a/tests/cases/fourslash/quickInfoNestedGenericCalls.ts b/tests/cases/fourslash/quickInfoNestedGenericCalls.ts new file mode 100644 index 0000000000000..c950728f48b08 --- /dev/null +++ b/tests/cases/fourslash/quickInfoNestedGenericCalls.ts @@ -0,0 +1,22 @@ +/// +// @strict: true +//// /*1*/m({ foo: /*2*/$("foo") }); +//// m({ foo: /*3*/$("foo") }); +//// declare const m: (s: { [_ in S]: { $: NoInfer } }) => void +//// declare const $: (s: T) => { $: S } +//// type NoInfer = [T][T extends any ? 0 : never]; + +verify.quickInfoAt("1", `const m: <"foo">(s: { + foo: { + $: "foo"; + }; +}) => void`); + +// the exact generic type params are not important in this test (they could change with changes to the inference algorithm) +// it's important though that they both display the same types +verify.quickInfoAt("2", `const $: (s: string) => { + $: unknown; +}`); +verify.quickInfoAt("3", `const $: (s: string) => { + $: unknown; +}`);