diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 631960f24ba08..6596404aac3ba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17331,7 +17331,6 @@ namespace ts { } function getReturnTypeFromBody(func: FunctionLikeDeclaration, checkMode?: CheckMode): Type { - const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); if (!func.body) { return unknownType; } @@ -17349,9 +17348,9 @@ namespace ts { } } else { - let types: Type[]; + let types = checkAndAggregateReturnExpressionTypes(func, checkMode); if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function - types = concatenate(checkAndAggregateYieldOperandTypes(func, checkMode), checkAndAggregateReturnExpressionTypes(func, checkMode)); + types = concatenate(checkAndAggregateYieldOperandTypes(func, checkMode), types); if (!types || types.length === 0) { const iterableIteratorAny = functionFlags & FunctionFlags.Async ? createAsyncIterableIteratorType(anyType) // AsyncGenerator function @@ -17364,7 +17363,6 @@ namespace ts { } } else { - types = checkAndAggregateReturnExpressionTypes(func, checkMode); if (!types) { // For an async function, the return type will not be never, but rather a Promise for never. return functionFlags & FunctionFlags.Async @@ -17388,6 +17386,7 @@ namespace ts { } } + const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); if (!contextualSignature) { reportErrorsFromWidening(func, type); } @@ -17457,7 +17456,8 @@ namespace ts { return true; } - function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] { + /** NOTE: Return value of `[]` means a different thing than `undefined`. `[]` means return `void`, `undefined` means return `never`. */ + function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] | undefined { const functionFlags = getFunctionFlags(func); const aggregatedTypes: Type[] = []; let hasReturnWithNoExpression = functionHasImplicitReturn(func); @@ -17482,8 +17482,7 @@ namespace ts { hasReturnWithNoExpression = true; } }); - if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || - func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction)) { + if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || mayReturnNever(func))) { return undefined; } if (strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression) { @@ -17491,6 +17490,17 @@ namespace ts { } return aggregatedTypes; } + function mayReturnNever(func: FunctionLikeDeclaration): boolean { + switch (func.kind) { + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + return true; + case SyntaxKind.MethodDeclaration: + return func.parent.kind === SyntaxKind.ObjectLiteralExpression; + default: + return false; + } + } /** * TypeScript Specification 1.0 (6.3) - July 2014 diff --git a/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.js b/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.js new file mode 100644 index 0000000000000..1f5077918028a --- /dev/null +++ b/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.js @@ -0,0 +1,7 @@ +//// [contextualSignature_objectLiteralMethodMayReturnNever.ts] +interface I { m(): number; } +const o: I = { m() { throw new Error("not implemented"); } }; + + +//// [contextualSignature_objectLiteralMethodMayReturnNever.js] +var o = { m: function () { throw new Error("not implemented"); } }; diff --git a/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.symbols b/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.symbols new file mode 100644 index 0000000000000..dd58d4d35ed4f --- /dev/null +++ b/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.symbols @@ -0,0 +1,11 @@ +=== tests/cases/compiler/contextualSignature_objectLiteralMethodMayReturnNever.ts === +interface I { m(): number; } +>I : Symbol(I, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 0, 0)) +>m : Symbol(I.m, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 0, 13)) + +const o: I = { m() { throw new Error("not implemented"); } }; +>o : Symbol(o, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 1, 5)) +>I : Symbol(I, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 0, 0)) +>m : Symbol(m, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 1, 14)) +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.types b/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.types new file mode 100644 index 0000000000000..aa1da8edcfcce --- /dev/null +++ b/tests/baselines/reference/contextualSignature_objectLiteralMethodMayReturnNever.types @@ -0,0 +1,14 @@ +=== tests/cases/compiler/contextualSignature_objectLiteralMethodMayReturnNever.ts === +interface I { m(): number; } +>I : I +>m : () => number + +const o: I = { m() { throw new Error("not implemented"); } }; +>o : I +>I : I +>{ m() { throw new Error("not implemented"); } } : { m(): never; } +>m : () => never +>new Error("not implemented") : Error +>Error : ErrorConstructor +>"not implemented" : "not implemented" + diff --git a/tests/baselines/reference/parserArrowFunctionExpression7.types b/tests/baselines/reference/parserArrowFunctionExpression7.types index 072a1548bd289..9edac3b84748c 100644 --- a/tests/baselines/reference/parserArrowFunctionExpression7.types +++ b/tests/baselines/reference/parserArrowFunctionExpression7.types @@ -1,10 +1,10 @@ === tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression7.ts === ({ ->({ async m() { for (;;) { } }}) : { m(): Promise; } ->{ async m() { for (;;) { } }} : { m(): Promise; } +>({ async m() { for (;;) { } }}) : { m(): Promise; } +>{ async m() { for (;;) { } }} : { m(): Promise; } async m() { ->m : () => Promise +>m : () => Promise for (;;) { } diff --git a/tests/baselines/reference/throwInEnclosingStatements.types b/tests/baselines/reference/throwInEnclosingStatements.types index e031d00147d96..d46307b0ea2f4 100644 --- a/tests/baselines/reference/throwInEnclosingStatements.types +++ b/tests/baselines/reference/throwInEnclosingStatements.types @@ -92,15 +92,15 @@ class C { } var aa = { ->aa : { id: number; biz(): void; } ->{ id:12, biz() { throw this; }} : { id: number; biz(): void; } +>aa : { id: number; biz(): never; } +>{ id:12, biz() { throw this; }} : { id: number; biz(): never; } id:12, >id : number >12 : 12 biz() { ->biz : () => void +>biz : () => never throw this; >this : any diff --git a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments3.types b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments3.types index aa38d2ccf8ac5..32f483227d3c6 100644 --- a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments3.types +++ b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments3.types @@ -130,8 +130,8 @@ function createLink(func: (a: number)=>void) { >o1 : JSX.Element >{}} /> : JSX.Element >Link : { (l: { func: (arg: U) => void; }): JSX.Element; (l: { func: (arg1: U, arg2: string) => void; }): JSX.Element; } ->func : (a: number, b: string) => any ->(a:number, b:string)=>{} : (a: number, b: string) => any +>func : (a: number, b: string) => void +>(a:number, b:string)=>{} : (a: number, b: string) => void >a : number >b : string } diff --git a/tests/cases/compiler/contextualSignature_objectLiteralMethodMayReturnNever.ts b/tests/cases/compiler/contextualSignature_objectLiteralMethodMayReturnNever.ts new file mode 100644 index 0000000000000..2536f67f6c42b --- /dev/null +++ b/tests/cases/compiler/contextualSignature_objectLiteralMethodMayReturnNever.ts @@ -0,0 +1,2 @@ +interface I { m(): number; } +const o: I = { m() { throw new Error("not implemented"); } };