Merge pull request #7450 from Microsoft/noImplicitReturnsInAsync

unwrap promised typed in async function before doing 'noImplicitRetur…
This commit is contained in:
Vladimir Matveev 2016-03-09 12:42:10 -08:00
commit 5d5fee2ef1
8 changed files with 257 additions and 7 deletions

View File

@ -10860,11 +10860,11 @@ namespace ts {
// If return type annotation is omitted check if function has any explicit return statements.
// If it does not have any - its inferred return type is void - don't do any checks.
// Otherwise get inferred return type from function body and report error only if it is not void / anytype
const inferredReturnType = hasExplicitReturn
? getReturnTypeOfSignature(getSignatureFromDeclaration(func))
: voidType;
if (inferredReturnType === voidType || isTypeAny(inferredReturnType)) {
if (!hasExplicitReturn) {
return;
}
const inferredReturnType = getReturnTypeOfSignature(getSignatureFromDeclaration(func));
if (isUnwrappedReturnTypeVoidOrAny(func, inferredReturnType)) {
return;
}
}
@ -12743,7 +12743,7 @@ namespace ts {
return checkNonThenableType(type, location, message);
}
else {
if (type.id === promisedType.id || awaitedTypeStack.indexOf(promisedType.id) >= 0) {
if (type.id === promisedType.id || indexOf(awaitedTypeStack, promisedType.id) >= 0) {
// We have a bad actor in the form of a promise whose promised type is
// the same promise type, or a mutually recursive promise. Return the
// unknown type as we cannot guess the shape. If this were the actual
@ -13896,6 +13896,11 @@ namespace ts {
return !!(node.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(<AccessorDeclaration>getDeclarationOfKind(node.symbol, SyntaxKind.SetAccessor)));
}
function isUnwrappedReturnTypeVoidOrAny(func: FunctionLikeDeclaration, returnType: Type): boolean {
const unwrappedReturnType = isAsyncFunctionLike(func) ? getPromisedType(returnType) : returnType;
return maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.Any);
}
function checkReturnStatement(node: ReturnStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
@ -13944,7 +13949,7 @@ namespace ts {
}
}
}
else if (compilerOptions.noImplicitReturns && !maybeTypeOfKind(returnType, TypeFlags.Void | TypeFlags.Any)) {
else if (compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) {
// The function has a return type, but the return statement doesn't have an expression.
error(node, Diagnostics.Not_all_code_paths_return_a_value);
}

View File

@ -0,0 +1,26 @@
//// [noImplicitReturnsInAsync1.ts]
async function test(isError: boolean = false) {
if (isError === true) {
return;
}
let x = await Promise.resolve("The test is passed without an error.");
}
//// [noImplicitReturnsInAsync1.js]
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
});
};
function test(isError = false) {
return __awaiter(this, void 0, void 0, function* () {
if (isError === true) {
return;
}
let x = yield Promise.resolve("The test is passed without an error.");
});
}

View File

@ -0,0 +1,17 @@
=== tests/cases/compiler/noImplicitReturnsInAsync1.ts ===
async function test(isError: boolean = false) {
>test : Symbol(test, Decl(noImplicitReturnsInAsync1.ts, 0, 0))
>isError : Symbol(isError, Decl(noImplicitReturnsInAsync1.ts, 1, 20))
if (isError === true) {
>isError : Symbol(isError, Decl(noImplicitReturnsInAsync1.ts, 1, 20))
return;
}
let x = await Promise.resolve("The test is passed without an error.");
>x : Symbol(x, Decl(noImplicitReturnsInAsync1.ts, 5, 7))
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
}

View File

@ -0,0 +1,23 @@
=== tests/cases/compiler/noImplicitReturnsInAsync1.ts ===
async function test(isError: boolean = false) {
>test : (isError?: boolean) => Promise<void>
>isError : boolean
>false : boolean
if (isError === true) {
>isError === true : boolean
>isError : boolean
>true : boolean
return;
}
let x = await Promise.resolve("The test is passed without an error.");
>x : string
>await Promise.resolve("The test is passed without an error.") : string
>Promise.resolve("The test is passed without an error.") : Promise<string>
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
>Promise : PromiseConstructor
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
>"The test is passed without an error." : string
}

View File

@ -0,0 +1,45 @@
tests/cases/compiler/noImplicitReturnsInAsync2.ts(3,16): error TS7030: Not all code paths return a value.
tests/cases/compiler/noImplicitReturnsInAsync2.ts(25,48): error TS7030: Not all code paths return a value.
==== tests/cases/compiler/noImplicitReturnsInAsync2.ts (2 errors) ====
// Should be an error, Promise<number>, currently retorted correctly
async function test3(isError: boolean = true) {
~~~~~
!!! error TS7030: Not all code paths return a value.
if (isError === true) {
return 6;
}
}
// Should not be an error, Promise<any>, currently **not** working
async function test4(isError: boolean = true) {
if (isError === true) {
return undefined;
}
}
// should not be error, Promise<any> currently working correctly
async function test5(isError: boolean = true): Promise<any> { //should not be error
if (isError === true) {
return undefined;
}
}
// should be error, currently reported correctly
async function test6(isError: boolean = true): Promise<number> {
~~~~~~~~~~~~~~~
!!! error TS7030: Not all code paths return a value.
if (isError === true) {
return undefined;
}
}
// infered to be Promise<void>, should not be an error, currently reported correctly
async function test7(isError: boolean = true) {
if (isError === true) {
return;
}
}

View File

@ -0,0 +1,87 @@
//// [noImplicitReturnsInAsync2.ts]
// Should be an error, Promise<number>, currently retorted correctly
async function test3(isError: boolean = true) {
if (isError === true) {
return 6;
}
}
// Should not be an error, Promise<any>, currently **not** working
async function test4(isError: boolean = true) {
if (isError === true) {
return undefined;
}
}
// should not be error, Promise<any> currently working correctly
async function test5(isError: boolean = true): Promise<any> { //should not be error
if (isError === true) {
return undefined;
}
}
// should be error, currently reported correctly
async function test6(isError: boolean = true): Promise<number> {
if (isError === true) {
return undefined;
}
}
// infered to be Promise<void>, should not be an error, currently reported correctly
async function test7(isError: boolean = true) {
if (isError === true) {
return;
}
}
//// [noImplicitReturnsInAsync2.js]
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
});
};
// Should be an error, Promise<number>, currently retorted correctly
function test3(isError = true) {
return __awaiter(this, void 0, void 0, function* () {
if (isError === true) {
return 6;
}
});
}
// Should not be an error, Promise<any>, currently **not** working
function test4(isError = true) {
return __awaiter(this, void 0, void 0, function* () {
if (isError === true) {
return undefined;
}
});
}
// should not be error, Promise<any> currently working correctly
function test5(isError = true) {
return __awaiter(this, void 0, void 0, function* () {
if (isError === true) {
return undefined;
}
});
}
// should be error, currently reported correctly
function test6(isError = true) {
return __awaiter(this, void 0, void 0, function* () {
if (isError === true) {
return undefined;
}
});
}
// infered to be Promise<void>, should not be an error, currently reported correctly
function test7(isError = true) {
return __awaiter(this, void 0, void 0, function* () {
if (isError === true) {
return;
}
});
}

View File

@ -0,0 +1,9 @@
// @target: es6
// @noImplicitReturns: true
async function test(isError: boolean = false) {
if (isError === true) {
return;
}
let x = await Promise.resolve("The test is passed without an error.");
}

View File

@ -0,0 +1,38 @@
// @target: es6
// @noImplicitReturns: true
// Should be an error, Promise<number>, currently retorted correctly
async function test3(isError: boolean = true) {
if (isError === true) {
return 6;
}
}
// Should not be an error, Promise<any>, currently **not** working
async function test4(isError: boolean = true) {
if (isError === true) {
return undefined;
}
}
// should not be error, Promise<any> currently working correctly
async function test5(isError: boolean = true): Promise<any> { //should not be error
if (isError === true) {
return undefined;
}
}
// should be error, currently reported correctly
async function test6(isError: boolean = true): Promise<number> {
if (isError === true) {
return undefined;
}
}
// infered to be Promise<void>, should not be an error, currently reported correctly
async function test7(isError: boolean = true) {
if (isError === true) {
return;
}
}