diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bb65f5f378..762b3cc41a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26129,15 +26129,19 @@ namespace ts { if (checkMode && checkMode & CheckMode.SkipContextSensitive && isContextSensitive(node)) { // Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage if (!getEffectiveReturnTypeNode(node) && hasContextSensitiveReturnExpression(node)) { - const links = getNodeLinks(node); - if (links.contextFreeType) { - return links.contextFreeType; + // Return plain anyFunctionType if there is no possibility we'll make inferences from the return type + const contextualSignature = getContextualSignature(node); + if (contextualSignature && couldContainTypeVariables(getReturnTypeOfSignature(contextualSignature))) { + const links = getNodeLinks(node); + if (links.contextFreeType) { + return links.contextFreeType; + } + const returnType = getReturnTypeFromBody(node, checkMode); + const returnOnlySignature = createSignature(undefined, undefined, undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); + const returnOnlyType = createAnonymousType(node.symbol, emptySymbols, [returnOnlySignature], emptyArray, undefined, undefined); + returnOnlyType.objectFlags |= ObjectFlags.NonInferrableType; + return links.contextFreeType = returnOnlyType; } - const returnType = getReturnTypeFromBody(node, checkMode); - const returnOnlySignature = createSignature(undefined, undefined, undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); - const returnOnlyType = createAnonymousType(node.symbol, emptySymbols, [returnOnlySignature], emptyArray, undefined, undefined); - returnOnlyType.objectFlags |= ObjectFlags.NonInferrableType; - return links.contextFreeType = returnOnlyType; } return anyFunctionType; } diff --git a/tests/baselines/reference/contextSensitiveReturnTypeInference.js b/tests/baselines/reference/contextSensitiveReturnTypeInference.js new file mode 100644 index 0000000000..7fae967ba4 --- /dev/null +++ b/tests/baselines/reference/contextSensitiveReturnTypeInference.js @@ -0,0 +1,87 @@ +//// [contextSensitiveReturnTypeInference.ts] +// Repro from #34849 + +interface IData { + bar: boolean +} + +declare function test( + getter: (deps: TDependencies, data: IData) => any, + deps: TDependencies, +): any + +const DEPS = { + foo: 1 +} + +test( + (deps, data) => ({ + fn1: function() { return deps.foo }, + fn2: data.bar + }), + DEPS +); + +test( + (deps: typeof DEPS, data) => ({ + fn1: function() { return deps.foo }, + fn2: data.bar + }), + DEPS +); + +test( + (deps, data) => ({ + fn1: () => deps.foo, + fn2: data.bar + }), + DEPS +); + +test( + (deps, data) => { + return { + fn1() { return deps.foo }, + fn2: data.bar + } + }, + DEPS +); + +test( + (deps) => ({ + fn1() { return deps.foo }, + fn2: 1 + }), + DEPS +); + + +//// [contextSensitiveReturnTypeInference.js] +"use strict"; +// Repro from #34849 +var DEPS = { + foo: 1 +}; +test(function (deps, data) { return ({ + fn1: function () { return deps.foo; }, + fn2: data.bar +}); }, DEPS); +test(function (deps, data) { return ({ + fn1: function () { return deps.foo; }, + fn2: data.bar +}); }, DEPS); +test(function (deps, data) { return ({ + fn1: function () { return deps.foo; }, + fn2: data.bar +}); }, DEPS); +test(function (deps, data) { + return { + fn1: function () { return deps.foo; }, + fn2: data.bar + }; +}, DEPS); +test(function (deps) { return ({ + fn1: function () { return deps.foo; }, + fn2: 1 +}); }, DEPS); diff --git a/tests/baselines/reference/contextSensitiveReturnTypeInference.symbols b/tests/baselines/reference/contextSensitiveReturnTypeInference.symbols new file mode 100644 index 0000000000..0a4da7d42e --- /dev/null +++ b/tests/baselines/reference/contextSensitiveReturnTypeInference.symbols @@ -0,0 +1,157 @@ +=== tests/cases/compiler/contextSensitiveReturnTypeInference.ts === +// Repro from #34849 + +interface IData { +>IData : Symbol(IData, Decl(contextSensitiveReturnTypeInference.ts, 0, 0)) + + bar: boolean +>bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) +} + +declare function test( +>test : Symbol(test, Decl(contextSensitiveReturnTypeInference.ts, 4, 1)) +>TDependencies : Symbol(TDependencies, Decl(contextSensitiveReturnTypeInference.ts, 6, 22)) + + getter: (deps: TDependencies, data: IData) => any, +>getter : Symbol(getter, Decl(contextSensitiveReturnTypeInference.ts, 6, 37)) +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 7, 11)) +>TDependencies : Symbol(TDependencies, Decl(contextSensitiveReturnTypeInference.ts, 6, 22)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 7, 31)) +>IData : Symbol(IData, Decl(contextSensitiveReturnTypeInference.ts, 0, 0)) + + deps: TDependencies, +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 7, 52)) +>TDependencies : Symbol(TDependencies, Decl(contextSensitiveReturnTypeInference.ts, 6, 22)) + +): any + +const DEPS = { +>DEPS : Symbol(DEPS, Decl(contextSensitiveReturnTypeInference.ts, 11, 5)) + + foo: 1 +>foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) +} + +test( +>test : Symbol(test, Decl(contextSensitiveReturnTypeInference.ts, 4, 1)) + + (deps, data) => ({ +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 16, 3)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 16, 8)) + + fn1: function() { return deps.foo }, +>fn1 : Symbol(fn1, Decl(contextSensitiveReturnTypeInference.ts, 16, 20)) +>deps.foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 16, 3)) +>foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) + + fn2: data.bar +>fn2 : Symbol(fn2, Decl(contextSensitiveReturnTypeInference.ts, 17, 40)) +>data.bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 16, 8)) +>bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) + + }), + DEPS +>DEPS : Symbol(DEPS, Decl(contextSensitiveReturnTypeInference.ts, 11, 5)) + +); + +test( +>test : Symbol(test, Decl(contextSensitiveReturnTypeInference.ts, 4, 1)) + + (deps: typeof DEPS, data) => ({ +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 24, 3)) +>DEPS : Symbol(DEPS, Decl(contextSensitiveReturnTypeInference.ts, 11, 5)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 24, 21)) + + fn1: function() { return deps.foo }, +>fn1 : Symbol(fn1, Decl(contextSensitiveReturnTypeInference.ts, 24, 33)) +>deps.foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 24, 3)) +>foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) + + fn2: data.bar +>fn2 : Symbol(fn2, Decl(contextSensitiveReturnTypeInference.ts, 25, 40)) +>data.bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 24, 21)) +>bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) + + }), + DEPS +>DEPS : Symbol(DEPS, Decl(contextSensitiveReturnTypeInference.ts, 11, 5)) + +); + +test( +>test : Symbol(test, Decl(contextSensitiveReturnTypeInference.ts, 4, 1)) + + (deps, data) => ({ +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 32, 3)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 32, 8)) + + fn1: () => deps.foo, +>fn1 : Symbol(fn1, Decl(contextSensitiveReturnTypeInference.ts, 32, 20)) +>deps.foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 32, 3)) +>foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) + + fn2: data.bar +>fn2 : Symbol(fn2, Decl(contextSensitiveReturnTypeInference.ts, 33, 24)) +>data.bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 32, 8)) +>bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) + + }), + DEPS +>DEPS : Symbol(DEPS, Decl(contextSensitiveReturnTypeInference.ts, 11, 5)) + +); + +test( +>test : Symbol(test, Decl(contextSensitiveReturnTypeInference.ts, 4, 1)) + + (deps, data) => { +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 40, 3)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 40, 8)) + + return { + fn1() { return deps.foo }, +>fn1 : Symbol(fn1, Decl(contextSensitiveReturnTypeInference.ts, 41, 12)) +>deps.foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 40, 3)) +>foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) + + fn2: data.bar +>fn2 : Symbol(fn2, Decl(contextSensitiveReturnTypeInference.ts, 42, 32)) +>data.bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) +>data : Symbol(data, Decl(contextSensitiveReturnTypeInference.ts, 40, 8)) +>bar : Symbol(IData.bar, Decl(contextSensitiveReturnTypeInference.ts, 2, 17)) + } + }, + DEPS +>DEPS : Symbol(DEPS, Decl(contextSensitiveReturnTypeInference.ts, 11, 5)) + +); + +test( +>test : Symbol(test, Decl(contextSensitiveReturnTypeInference.ts, 4, 1)) + + (deps) => ({ +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 50, 3)) + + fn1() { return deps.foo }, +>fn1 : Symbol(fn1, Decl(contextSensitiveReturnTypeInference.ts, 50, 14)) +>deps.foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) +>deps : Symbol(deps, Decl(contextSensitiveReturnTypeInference.ts, 50, 3)) +>foo : Symbol(foo, Decl(contextSensitiveReturnTypeInference.ts, 11, 14)) + + fn2: 1 +>fn2 : Symbol(fn2, Decl(contextSensitiveReturnTypeInference.ts, 51, 30)) + + }), + DEPS +>DEPS : Symbol(DEPS, Decl(contextSensitiveReturnTypeInference.ts, 11, 5)) + +); + diff --git a/tests/baselines/reference/contextSensitiveReturnTypeInference.types b/tests/baselines/reference/contextSensitiveReturnTypeInference.types new file mode 100644 index 0000000000..c0638e7b53 --- /dev/null +++ b/tests/baselines/reference/contextSensitiveReturnTypeInference.types @@ -0,0 +1,177 @@ +=== tests/cases/compiler/contextSensitiveReturnTypeInference.ts === +// Repro from #34849 + +interface IData { + bar: boolean +>bar : boolean +} + +declare function test( +>test : (getter: (deps: TDependencies, data: IData) => any, deps: TDependencies) => any + + getter: (deps: TDependencies, data: IData) => any, +>getter : (deps: TDependencies, data: IData) => any +>deps : TDependencies +>data : IData + + deps: TDependencies, +>deps : TDependencies + +): any + +const DEPS = { +>DEPS : { foo: number; } +>{ foo: 1} : { foo: number; } + + foo: 1 +>foo : number +>1 : 1 +} + +test( +>test( (deps, data) => ({ fn1: function() { return deps.foo }, fn2: data.bar }), DEPS) : any +>test : (getter: (deps: TDependencies, data: IData) => any, deps: TDependencies) => any + + (deps, data) => ({ +>(deps, data) => ({ fn1: function() { return deps.foo }, fn2: data.bar }) : (deps: { foo: number; }, data: IData) => { fn1: () => number; fn2: boolean; } +>deps : { foo: number; } +>data : IData +>({ fn1: function() { return deps.foo }, fn2: data.bar }) : { fn1: () => number; fn2: boolean; } +>{ fn1: function() { return deps.foo }, fn2: data.bar } : { fn1: () => number; fn2: boolean; } + + fn1: function() { return deps.foo }, +>fn1 : () => number +>function() { return deps.foo } : () => number +>deps.foo : number +>deps : { foo: number; } +>foo : number + + fn2: data.bar +>fn2 : boolean +>data.bar : boolean +>data : IData +>bar : boolean + + }), + DEPS +>DEPS : { foo: number; } + +); + +test( +>test( (deps: typeof DEPS, data) => ({ fn1: function() { return deps.foo }, fn2: data.bar }), DEPS) : any +>test : (getter: (deps: TDependencies, data: IData) => any, deps: TDependencies) => any + + (deps: typeof DEPS, data) => ({ +>(deps: typeof DEPS, data) => ({ fn1: function() { return deps.foo }, fn2: data.bar }) : (deps: { foo: number; }, data: IData) => { fn1: () => number; fn2: boolean; } +>deps : { foo: number; } +>DEPS : { foo: number; } +>data : IData +>({ fn1: function() { return deps.foo }, fn2: data.bar }) : { fn1: () => number; fn2: boolean; } +>{ fn1: function() { return deps.foo }, fn2: data.bar } : { fn1: () => number; fn2: boolean; } + + fn1: function() { return deps.foo }, +>fn1 : () => number +>function() { return deps.foo } : () => number +>deps.foo : number +>deps : { foo: number; } +>foo : number + + fn2: data.bar +>fn2 : boolean +>data.bar : boolean +>data : IData +>bar : boolean + + }), + DEPS +>DEPS : { foo: number; } + +); + +test( +>test( (deps, data) => ({ fn1: () => deps.foo, fn2: data.bar }), DEPS) : any +>test : (getter: (deps: TDependencies, data: IData) => any, deps: TDependencies) => any + + (deps, data) => ({ +>(deps, data) => ({ fn1: () => deps.foo, fn2: data.bar }) : (deps: { foo: number; }, data: IData) => { fn1: () => number; fn2: boolean; } +>deps : { foo: number; } +>data : IData +>({ fn1: () => deps.foo, fn2: data.bar }) : { fn1: () => number; fn2: boolean; } +>{ fn1: () => deps.foo, fn2: data.bar } : { fn1: () => number; fn2: boolean; } + + fn1: () => deps.foo, +>fn1 : () => number +>() => deps.foo : () => number +>deps.foo : number +>deps : { foo: number; } +>foo : number + + fn2: data.bar +>fn2 : boolean +>data.bar : boolean +>data : IData +>bar : boolean + + }), + DEPS +>DEPS : { foo: number; } + +); + +test( +>test( (deps, data) => { return { fn1() { return deps.foo }, fn2: data.bar } }, DEPS) : any +>test : (getter: (deps: TDependencies, data: IData) => any, deps: TDependencies) => any + + (deps, data) => { +>(deps, data) => { return { fn1() { return deps.foo }, fn2: data.bar } } : (deps: { foo: number; }, data: IData) => { fn1(): number; fn2: boolean; } +>deps : { foo: number; } +>data : IData + + return { +>{ fn1() { return deps.foo }, fn2: data.bar } : { fn1(): number; fn2: boolean; } + + fn1() { return deps.foo }, +>fn1 : () => number +>deps.foo : number +>deps : { foo: number; } +>foo : number + + fn2: data.bar +>fn2 : boolean +>data.bar : boolean +>data : IData +>bar : boolean + } + }, + DEPS +>DEPS : { foo: number; } + +); + +test( +>test( (deps) => ({ fn1() { return deps.foo }, fn2: 1 }), DEPS) : any +>test : (getter: (deps: TDependencies, data: IData) => any, deps: TDependencies) => any + + (deps) => ({ +>(deps) => ({ fn1() { return deps.foo }, fn2: 1 }) : (deps: { foo: number; }) => { fn1(): number; fn2: number; } +>deps : { foo: number; } +>({ fn1() { return deps.foo }, fn2: 1 }) : { fn1(): number; fn2: number; } +>{ fn1() { return deps.foo }, fn2: 1 } : { fn1(): number; fn2: number; } + + fn1() { return deps.foo }, +>fn1 : () => number +>deps.foo : number +>deps : { foo: number; } +>foo : number + + fn2: 1 +>fn2 : number +>1 : 1 + + }), + DEPS +>DEPS : { foo: number; } + +); + diff --git a/tests/cases/compiler/contextSensitiveReturnTypeInference.ts b/tests/cases/compiler/contextSensitiveReturnTypeInference.ts new file mode 100644 index 0000000000..8a456baf90 --- /dev/null +++ b/tests/cases/compiler/contextSensitiveReturnTypeInference.ts @@ -0,0 +1,58 @@ +// @strict: true + +// Repro from #34849 + +interface IData { + bar: boolean +} + +declare function test( + getter: (deps: TDependencies, data: IData) => any, + deps: TDependencies, +): any + +const DEPS = { + foo: 1 +} + +test( + (deps, data) => ({ + fn1: function() { return deps.foo }, + fn2: data.bar + }), + DEPS +); + +test( + (deps: typeof DEPS, data) => ({ + fn1: function() { return deps.foo }, + fn2: data.bar + }), + DEPS +); + +test( + (deps, data) => ({ + fn1: () => deps.foo, + fn2: data.bar + }), + DEPS +); + +test( + (deps, data) => { + return { + fn1() { return deps.foo }, + fn2: data.bar + } + }, + DEPS +); + +test( + (deps) => ({ + fn1() { return deps.foo }, + fn2: 1 + }), + DEPS +);