Skip to content

Commit c6e4373

Browse files
author
Andy
authored
In checkAndAggregateReturnExpressionTypes, treat MethodDeclaration in an object literal same as a FunctionExpression (microsoft#20052)
* In checkAndAggregateReturnExpressionTypes, treat MethodDeclaration in an object literal same as a FunctionExpression * Add original test case
1 parent 5bd477e commit c6e4373

8 files changed

+59
-15
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17985,7 +17985,6 @@ namespace ts {
1798517985
}
1798617986

1798717987
function getReturnTypeFromBody(func: FunctionLikeDeclaration, checkMode?: CheckMode): Type {
17988-
const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func);
1798917988
if (!func.body) {
1799017989
return unknownType;
1799117990
}
@@ -18003,9 +18002,9 @@ namespace ts {
1800318002
}
1800418003
}
1800518004
else {
18006-
let types: Type[];
18005+
let types = checkAndAggregateReturnExpressionTypes(func, checkMode);
1800718006
if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function
18008-
types = concatenate(checkAndAggregateYieldOperandTypes(func, checkMode), checkAndAggregateReturnExpressionTypes(func, checkMode));
18007+
types = concatenate(checkAndAggregateYieldOperandTypes(func, checkMode), types);
1800918008
if (!types || types.length === 0) {
1801018009
const iterableIteratorAny = functionFlags & FunctionFlags.Async
1801118010
? createAsyncIterableIteratorType(anyType) // AsyncGenerator function
@@ -18018,7 +18017,6 @@ namespace ts {
1801818017
}
1801918018
}
1802018019
else {
18021-
types = checkAndAggregateReturnExpressionTypes(func, checkMode);
1802218020
if (!types) {
1802318021
// For an async function, the return type will not be never, but rather a Promise for never.
1802418022
return functionFlags & FunctionFlags.Async
@@ -18037,6 +18035,7 @@ namespace ts {
1803718035
type = getUnionType(types, UnionReduction.Subtype);
1803818036
}
1803918037

18038+
const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func);
1804018039
if (!contextualSignature) {
1804118040
reportErrorsFromWidening(func, type);
1804218041
}
@@ -18126,7 +18125,8 @@ namespace ts {
1812618125
return true;
1812718126
}
1812818127

18129-
function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] {
18128+
/** NOTE: Return value of `[]` means a different thing than `undefined`. `[]` means return `void`, `undefined` means return `never`. */
18129+
function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] | undefined {
1813018130
const functionFlags = getFunctionFlags(func);
1813118131
const aggregatedTypes: Type[] = [];
1813218132
let hasReturnWithNoExpression = functionHasImplicitReturn(func);
@@ -18151,15 +18151,25 @@ namespace ts {
1815118151
hasReturnWithNoExpression = true;
1815218152
}
1815318153
});
18154-
if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever ||
18155-
func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction)) {
18154+
if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || mayReturnNever(func))) {
1815618155
return undefined;
1815718156
}
1815818157
if (strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression) {
1815918158
pushIfUnique(aggregatedTypes, undefinedType);
1816018159
}
1816118160
return aggregatedTypes;
1816218161
}
18162+
function mayReturnNever(func: FunctionLikeDeclaration): boolean {
18163+
switch (func.kind) {
18164+
case SyntaxKind.FunctionExpression:
18165+
case SyntaxKind.ArrowFunction:
18166+
return true;
18167+
case SyntaxKind.MethodDeclaration:
18168+
return func.parent.kind === SyntaxKind.ObjectLiteralExpression;
18169+
default:
18170+
return false;
18171+
}
18172+
}
1816318173

1816418174
/**
1816518175
* TypeScript Specification 1.0 (6.3) - July 2014
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//// [contextualSignature_objectLiteralMethodMayReturnNever.ts]
2+
interface I { m(): number; }
3+
const o: I = { m() { throw new Error("not implemented"); } };
4+
5+
6+
//// [contextualSignature_objectLiteralMethodMayReturnNever.js]
7+
var o = { m: function () { throw new Error("not implemented"); } };
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/compiler/contextualSignature_objectLiteralMethodMayReturnNever.ts ===
2+
interface I { m(): number; }
3+
>I : Symbol(I, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 0, 0))
4+
>m : Symbol(I.m, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 0, 13))
5+
6+
const o: I = { m() { throw new Error("not implemented"); } };
7+
>o : Symbol(o, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 1, 5))
8+
>I : Symbol(I, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 0, 0))
9+
>m : Symbol(m, Decl(contextualSignature_objectLiteralMethodMayReturnNever.ts, 1, 14))
10+
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
11+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/compiler/contextualSignature_objectLiteralMethodMayReturnNever.ts ===
2+
interface I { m(): number; }
3+
>I : I
4+
>m : () => number
5+
6+
const o: I = { m() { throw new Error("not implemented"); } };
7+
>o : I
8+
>I : I
9+
>{ m() { throw new Error("not implemented"); } } : { m(): never; }
10+
>m : () => never
11+
>new Error("not implemented") : Error
12+
>Error : ErrorConstructor
13+
>"not implemented" : "not implemented"
14+

tests/baselines/reference/parserArrowFunctionExpression7.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
=== tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression7.ts ===
22
({
3-
>({ async m() { for (;;) { } }}) : { m(): Promise<void>; }
4-
>{ async m() { for (;;) { } }} : { m(): Promise<void>; }
3+
>({ async m() { for (;;) { } }}) : { m(): Promise<never>; }
4+
>{ async m() { for (;;) { } }} : { m(): Promise<never>; }
55

66
async m() {
7-
>m : () => Promise<void>
7+
>m : () => Promise<never>
88

99
for (;;) {
1010
}

tests/baselines/reference/throwInEnclosingStatements.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,15 @@ class C<T> {
9292
}
9393

9494
var aa = {
95-
>aa : { id: number; biz(): void; }
96-
>{ id:12, biz() { throw this; }} : { id: number; biz(): void; }
95+
>aa : { id: number; biz(): never; }
96+
>{ id:12, biz() { throw this; }} : { id: number; biz(): never; }
9797

9898
id:12,
9999
>id : number
100100
>12 : 12
101101

102102
biz() {
103-
>biz : () => void
103+
>biz : () => never
104104

105105
throw this;
106106
>this : any

tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments3.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ function createLink(func: (a: number)=>void) {
130130
>o1 : JSX.Element
131131
><Link func={(a:number, b:string)=>{}} /> : JSX.Element
132132
>Link : { <U>(l: { func: (arg: U) => void; }): JSX.Element; <U>(l: { func: (arg1: U, arg2: string) => void; }): JSX.Element; }
133-
>func : (a: number, b: string) => any
134-
>(a:number, b:string)=>{} : (a: number, b: string) => any
133+
>func : (a: number, b: string) => void
134+
>(a:number, b:string)=>{} : (a: number, b: string) => void
135135
>a : number
136136
>b : string
137137
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
interface I { m(): number; }
2+
const o: I = { m() { throw new Error("not implemented"); } };

0 commit comments

Comments
 (0)