From 2762772afd6ad6d5bc8981e833bb15db815da069 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Mar 2016 09:58:47 -0800 Subject: [PATCH] Include 'undefined' in return type for implicit or expressionless returns --- src/compiler/checker.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9384145164e..e269dc380ca 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6290,6 +6290,14 @@ namespace ts { return true; } + function getCombinedFlagsOfTypes(types: Type[]) { + let flags: TypeFlags = 0; + for (const t of types) { + flags |= t.flags; + } + return flags; + } + function getCommonSupertype(types: Type[]): Type { if (!strictNullChecks) { return forEach(types, t => isSupertypeOfEach(t, types) ? t : undefined); @@ -6299,7 +6307,7 @@ namespace ts { return getUnionType(types); } const supertype = forEach(primaryTypes, t => isSupertypeOfEach(t, primaryTypes) ? t : undefined); - return supertype && addNullableKind(supertype, reduceLeft(types, (flags, t) => flags | t.flags, 0) & TypeFlags.Nullable); + return supertype && addNullableKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable); } function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void { @@ -10931,7 +10939,8 @@ namespace ts { } } else { - types = checkAndAggregateReturnExpressionTypes(func.body, contextualMapper, isAsync); + const hasImplicitReturn = !!(func.flags & NodeFlags.HasImplicitReturn); + types = checkAndAggregateReturnExpressionTypes(func.body, contextualMapper, isAsync, hasImplicitReturn); if (types.length === 0) { if (isAsync) { // For an async function, the return type will not be void, but rather a Promise for void. @@ -11011,9 +11020,9 @@ namespace ts { return aggregatedTypes; } - function checkAndAggregateReturnExpressionTypes(body: Block, contextualMapper?: TypeMapper, isAsync?: boolean): Type[] { + function checkAndAggregateReturnExpressionTypes(body: Block, contextualMapper: TypeMapper, isAsync: boolean, hasImplicitReturn: boolean): Type[] { const aggregatedTypes: Type[] = []; - + let hasOmittedExpressions = false; forEachReturnStatement(body, returnStatement => { const expr = returnStatement.expression; if (expr) { @@ -11025,13 +11034,19 @@ namespace ts { // the native Promise type by the caller. type = checkAwaitedType(type, body.parent, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); } - if (!contains(aggregatedTypes, type)) { aggregatedTypes.push(type); } } + else { + hasOmittedExpressions = true; + } }); - + if (strictNullChecks && aggregatedTypes.length && (hasOmittedExpressions || hasImplicitReturn)) { + if (!contains(aggregatedTypes, undefinedType)) { + aggregatedTypes.push(undefinedType); + } + } return aggregatedTypes; }