Explicit undefined return type checked similar to explicit void return type (#53607)

This commit is contained in:
Anders Hejlsberg
2023-04-01 14:44:52 -07:00
committed by GitHub
parent 27d3454f07
commit b40385b595
40 changed files with 1002 additions and 171 deletions

View File

@@ -35426,10 +35426,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
: neverType; // Normal function
}
if (types.length === 0) {
// For an async function, the return type will not be void, but rather a Promise for void.
return functionFlags & FunctionFlags.Async
? createPromiseReturnType(func, voidType) // Async function
: voidType; // Normal function
// For an async function, the return type will not be void/undefined, but rather a Promise for void/undefined.
const contextualReturnType = getContextualReturnType(func, /*contextFlags*/ undefined);
const returnType = contextualReturnType && (unwrapReturnType(contextualReturnType, functionFlags) || voidType).flags & TypeFlags.Undefined ? undefinedType : voidType;
return functionFlags & FunctionFlags.Async ? createPromiseReturnType(func, returnType) : // Async function
returnType; // Normal function
}
// Return a union of the return expression types.
@@ -35665,8 +35666,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const functionFlags = getFunctionFlags(func);
const type = returnType && unwrapReturnType(returnType, functionFlags);
// Functions with an explicitly specified 'undefined, 'void', 'any' or 'unknown' return type don't need any return expressions.
if (type && maybeTypeOfKind(type, TypeFlags.Undefined | TypeFlags.Void | TypeFlags.Any | TypeFlags.Unknown)) {
// Functions with an explicitly specified return type that includes `void` or is exactly `any` or `undefined` don't
// need any return statements.
if (type && (maybeTypeOfKind(type, TypeFlags.Void) || type.flags & (TypeFlags.Any | TypeFlags.Undefined))) {
return;
}
@@ -35685,14 +35687,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
else if (type && !hasExplicitReturn) {
// minimal check: function has syntactic return type annotation and no explicit return statements in the body
// this function does not conform to the specification.
if (strictNullChecks) {
error(errorNode, Diagnostics.A_function_whose_declared_type_is_neither_undefined_void_nor_any_must_return_a_value);
}
else {
error(errorNode, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
}
error(errorNode, Diagnostics.A_function_whose_declared_type_is_neither_undefined_void_nor_any_must_return_a_value);
}
else if (type && strictNullChecks) {
else if (type && strictNullChecks && !isTypeAssignableTo(undefinedType, type)) {
error(errorNode, Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined);
}
else if (compilerOptions.noImplicitReturns) {
@@ -35704,7 +35701,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return;
}
const inferredReturnType = getReturnTypeOfSignature(getSignatureFromDeclaration(func));
if (isUnwrappedReturnTypeVoidOrAny(func, inferredReturnType)) {
if (isUnwrappedReturnTypeUndefinedVoidOrAny(func, inferredReturnType)) {
return;
}
}
@@ -42182,9 +42179,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return isAsync ? getAwaitedTypeNoAlias(returnType) || errorType : returnType;
}
function isUnwrappedReturnTypeVoidOrAny(func: SignatureDeclaration, returnType: Type): boolean {
const unwrappedReturnType = unwrapReturnType(returnType, getFunctionFlags(func));
return !!unwrappedReturnType && maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.AnyOrUnknown);
function isUnwrappedReturnTypeUndefinedVoidOrAny(func: SignatureDeclaration, returnType: Type): boolean {
const type = unwrapReturnType(returnType, getFunctionFlags(func));
return !!(type && (maybeTypeOfKind(type, TypeFlags.Void) || type.flags & (TypeFlags.Any | TypeFlags.Undefined)));
}
function checkReturnStatement(node: ReturnStatement) {
@@ -42232,7 +42229,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
}
else if (container.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(container, returnType)) {
else if (container.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeUndefinedVoidOrAny(container, 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

@@ -1880,7 +1880,7 @@
"category": "Error",
"code": 2354
},
"A function whose declared type is neither 'void' nor 'any' must return a value.": {
"A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.": {
"category": "Error",
"code": 2355
},
@@ -3615,10 +3615,6 @@
"category": "Error",
"code": 2846
},
"A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.": {
"category": "Error",
"code": 2847
},
"The right-hand side of an 'instanceof' expression must not be an instantiation expression.": {
"category": "Error",
"code": 2848

View File

@@ -51,7 +51,6 @@ const fixIdAddReturnStatement = "fixAddReturnStatement";
const fixRemoveBracesFromArrowFunctionBody = "fixRemoveBracesFromArrowFunctionBody";
const fixIdWrapTheBlockWithParen = "fixWrapTheBlockWithParen";
const errorCodes = [
Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value.code,
Diagnostics.A_function_whose_declared_type_is_neither_undefined_void_nor_any_must_return_a_value.code,
Diagnostics.Type_0_is_not_assignable_to_type_1.code,
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code
@@ -214,7 +213,6 @@ function getInfo(checker: TypeChecker, sourceFile: SourceFile, position: number,
const declaration = findAncestor(node.parent, isFunctionLikeDeclaration);
switch (errorCode) {
case Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value.code:
case Diagnostics.A_function_whose_declared_type_is_neither_undefined_void_nor_any_must_return_a_value.code:
if (!declaration || !declaration.body || !declaration.type || !rangeContainsRange(declaration.type, node)) return undefined;
return getFixInfo(checker, declaration, checker.getTypeFromTypeNode(declaration.type), /*isFunctionType*/ false);