mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-08 18:11:45 -06:00
Merge pull request #16346 from Microsoft/fix15618
Improve contextual types using jsdoc tags
This commit is contained in:
commit
eadafd20eb
@ -4200,16 +4200,6 @@ namespace ts {
|
||||
|
||||
// Return the inferred type for a variable, parameter, or property declaration
|
||||
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type {
|
||||
if (declaration.flags & NodeFlags.JavaScriptFile) {
|
||||
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
|
||||
// one as its type), otherwise fallback to the below standard TS codepaths to
|
||||
// try to figure it out.
|
||||
const type = getTypeForDeclarationFromJSDocComment(declaration);
|
||||
if (type && type !== unknownType) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
// A variable declared in a for..in statement is of type string, or of type keyof T when the
|
||||
// right hand expression is of a type parameter type.
|
||||
if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
|
||||
@ -4231,8 +4221,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Use type from type annotation if one is present
|
||||
if (declaration.type) {
|
||||
const declaredType = getTypeFromTypeNode(declaration.type);
|
||||
const typeNode = getEffectiveTypeAnnotationNode(declaration);
|
||||
if (typeNode) {
|
||||
const declaredType = getTypeFromTypeNode(typeNode);
|
||||
return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality);
|
||||
}
|
||||
|
||||
@ -4523,10 +4514,11 @@ namespace ts {
|
||||
function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type {
|
||||
if (accessor) {
|
||||
if (accessor.kind === SyntaxKind.GetAccessor) {
|
||||
return accessor.type && getTypeFromTypeNode(accessor.type);
|
||||
const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor);
|
||||
return getterTypeAnnotation && getTypeFromTypeNode(getterTypeAnnotation);
|
||||
}
|
||||
else {
|
||||
const setterTypeAnnotation = getSetAccessorTypeAnnotationNode(accessor);
|
||||
const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor);
|
||||
return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation);
|
||||
}
|
||||
}
|
||||
@ -4679,7 +4671,7 @@ namespace ts {
|
||||
|
||||
function reportCircularityError(symbol: Symbol) {
|
||||
// Check if variable has type annotation that circularly references the variable itself
|
||||
if ((<VariableLikeDeclaration>symbol.valueDeclaration).type) {
|
||||
if (getEffectiveTypeAnnotationNode(<VariableLikeDeclaration>symbol.valueDeclaration)) {
|
||||
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
|
||||
symbolToString(symbol));
|
||||
return unknownType;
|
||||
@ -5265,14 +5257,18 @@ namespace ts {
|
||||
// A variable-like declaration is considered independent (free of this references) if it has a type annotation
|
||||
// that specifies an independent type, or if it has no type annotation and no initializer (and thus of type any).
|
||||
function isIndependentVariableLikeDeclaration(node: VariableLikeDeclaration): boolean {
|
||||
return node.type && isIndependentType(node.type) || !node.type && !node.initializer;
|
||||
const typeNode = getEffectiveTypeAnnotationNode(node);
|
||||
return typeNode ? isIndependentType(typeNode) : !node.initializer;
|
||||
}
|
||||
|
||||
// A function-like declaration is considered independent (free of this references) if it has a return type
|
||||
// annotation that is considered independent and if each parameter is considered independent.
|
||||
function isIndependentFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
|
||||
if (node.kind !== SyntaxKind.Constructor && (!node.type || !isIndependentType(node.type))) {
|
||||
return false;
|
||||
if (node.kind !== SyntaxKind.Constructor) {
|
||||
const typeNode = getEffectiveReturnTypeNode(node);
|
||||
if (!typeNode || !isIndependentType(typeNode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const parameter of node.parameters) {
|
||||
if (!isIndependentVariableLikeDeclaration(parameter)) {
|
||||
@ -6424,15 +6420,10 @@ namespace ts {
|
||||
else if (classType) {
|
||||
return classType;
|
||||
}
|
||||
else if (declaration.type) {
|
||||
return getTypeFromTypeNode(declaration.type);
|
||||
}
|
||||
|
||||
if (declaration.flags & NodeFlags.JavaScriptFile) {
|
||||
const type = getReturnTypeFromJSDocComment(declaration);
|
||||
if (type && type !== unknownType) {
|
||||
return type;
|
||||
}
|
||||
const typeNode = getEffectiveReturnTypeNode(declaration);
|
||||
if (typeNode) {
|
||||
return getTypeFromTypeNode(typeNode);
|
||||
}
|
||||
|
||||
// TypeScript 1.0 spec (April 2014):
|
||||
@ -8333,7 +8324,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
// Functions with any parameters that lack type annotations are context sensitive.
|
||||
if (forEach(node.parameters, p => !p.type)) {
|
||||
if (forEach(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) {
|
||||
return true;
|
||||
}
|
||||
// For arrow functions we now know we're not context sensitive.
|
||||
@ -12754,8 +12745,15 @@ namespace ts {
|
||||
function getContextualTypeForInitializerExpression(node: Expression): Type {
|
||||
const declaration = <VariableLikeDeclaration>node.parent;
|
||||
if (node === declaration.initializer) {
|
||||
if (declaration.type) {
|
||||
return getTypeFromTypeNode(declaration.type);
|
||||
const typeNode = getEffectiveTypeAnnotationNode(declaration);
|
||||
if (typeNode) {
|
||||
return getTypeFromTypeNode(typeNode);
|
||||
}
|
||||
if (isInJavaScriptFile(declaration)) {
|
||||
const jsDocType = getTypeForDeclarationFromJSDocComment(declaration);
|
||||
if (jsDocType) {
|
||||
return jsDocType;
|
||||
}
|
||||
}
|
||||
if (declaration.kind === SyntaxKind.Parameter) {
|
||||
const type = getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
|
||||
@ -12769,12 +12767,13 @@ namespace ts {
|
||||
if (isBindingPattern(declaration.parent)) {
|
||||
const parentDeclaration = declaration.parent.parent;
|
||||
const name = declaration.propertyName || declaration.name;
|
||||
if (parentDeclaration.kind !== SyntaxKind.BindingElement &&
|
||||
parentDeclaration.type &&
|
||||
!isBindingPattern(name)) {
|
||||
const text = getTextOfPropertyName(name);
|
||||
if (text) {
|
||||
return getTypeOfPropertyOfType(getTypeFromTypeNode(parentDeclaration.type), text);
|
||||
if (parentDeclaration.kind !== SyntaxKind.BindingElement) {
|
||||
const parentTypeNode = getEffectiveTypeAnnotationNode(parentDeclaration);
|
||||
if (parentTypeNode && !isBindingPattern(name)) {
|
||||
const text = getTextOfPropertyName(name);
|
||||
if (text) {
|
||||
return getTypeOfPropertyOfType(getTypeFromTypeNode(parentTypeNode), text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12828,9 +12827,9 @@ namespace ts {
|
||||
function getContextualReturnType(functionDecl: FunctionLikeDeclaration): Type {
|
||||
// If the containing function has a return type annotation, is a constructor, or is a get accessor whose
|
||||
// corresponding set accessor has a type annotation, return statements in the function are contextually typed
|
||||
if (functionDecl.type ||
|
||||
functionDecl.kind === SyntaxKind.Constructor ||
|
||||
functionDecl.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind<SetAccessorDeclaration>(functionDecl.symbol, SyntaxKind.SetAccessor))) {
|
||||
if (functionDecl.kind === SyntaxKind.Constructor ||
|
||||
getEffectiveReturnTypeNode(functionDecl) ||
|
||||
isGetAccessorWithAnnotatedSetAccessor(functionDecl)) {
|
||||
return getReturnTypeOfSignature(getSignatureFromDeclaration(functionDecl));
|
||||
}
|
||||
|
||||
@ -16364,8 +16363,9 @@ namespace ts {
|
||||
if (checkMode === CheckMode.Inferential) {
|
||||
for (let i = 0; i < len; i++) {
|
||||
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
|
||||
if (declaration.type) {
|
||||
inferTypes((<InferenceContext>mapper).inferences, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
|
||||
const typeNode = getEffectiveTypeAnnotationNode(declaration);
|
||||
if (typeNode) {
|
||||
inferTypes((<InferenceContext>mapper).inferences, getTypeFromTypeNode(typeNode), getTypeAtPosition(context, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16380,14 +16380,14 @@ namespace ts {
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
const parameter = signature.parameters[i];
|
||||
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
|
||||
if (!getEffectiveTypeAnnotationNode(<ParameterDeclaration>parameter.valueDeclaration)) {
|
||||
const contextualParameterType = getTypeAtPosition(context, i);
|
||||
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper, checkMode);
|
||||
}
|
||||
}
|
||||
if (signature.hasRestParameter && isRestParameterIndex(context, signature.parameters.length - 1)) {
|
||||
const parameter = lastOrUndefined(signature.parameters);
|
||||
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
|
||||
if (!getEffectiveTypeAnnotationNode(<ParameterDeclaration>parameter.valueDeclaration)) {
|
||||
const contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
|
||||
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper, checkMode);
|
||||
}
|
||||
@ -16455,15 +16455,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getReturnTypeFromJSDocComment(func: SignatureDeclaration | FunctionDeclaration): Type {
|
||||
const returnTag = getJSDocReturnTag(func);
|
||||
if (returnTag && returnTag.typeExpression) {
|
||||
return getTypeFromTypeNode(returnTag.typeExpression.type);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function createPromiseType(promisedType: Type): Type {
|
||||
// creates a `Promise<T>` type where `T` is the promisedType argument
|
||||
const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true);
|
||||
@ -16689,16 +16680,16 @@ namespace ts {
|
||||
const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn;
|
||||
|
||||
if (returnType && returnType.flags & TypeFlags.Never) {
|
||||
error(func.type, Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point);
|
||||
error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point);
|
||||
}
|
||||
else if (returnType && !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.
|
||||
// NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present
|
||||
error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
|
||||
error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
|
||||
}
|
||||
else if (returnType && strictNullChecks && !isTypeAssignableTo(undefinedType, returnType)) {
|
||||
error(func.type, Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined);
|
||||
error(getEffectiveReturnTypeNode(func), Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined);
|
||||
}
|
||||
else if (compilerOptions.noImplicitReturns) {
|
||||
if (!returnType) {
|
||||
@ -16713,7 +16704,7 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
}
|
||||
error(func.type || func, Diagnostics.Not_all_code_paths_return_a_value);
|
||||
error(getEffectiveReturnTypeNode(func) || func, Diagnostics.Not_all_code_paths_return_a_value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16753,7 +16744,7 @@ namespace ts {
|
||||
if (contextSensitive) {
|
||||
assignContextualParameterTypes(signature, contextualSignature, getContextualMapper(node), checkMode);
|
||||
}
|
||||
if (mightFixTypeParameters || !node.type && !signature.resolvedReturnType) {
|
||||
if (mightFixTypeParameters || !getEffectiveReturnTypeNode(node) && !signature.resolvedReturnType) {
|
||||
const returnType = getReturnTypeFromBody(node, checkMode);
|
||||
if (!signature.resolvedReturnType) {
|
||||
signature.resolvedReturnType = returnType;
|
||||
@ -16781,10 +16772,11 @@ namespace ts {
|
||||
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
|
||||
|
||||
const functionFlags = getFunctionFlags(node);
|
||||
const returnOrPromisedType = node.type &&
|
||||
const returnTypeNode = getEffectiveReturnTypeNode(node);
|
||||
const returnOrPromisedType = returnTypeNode &&
|
||||
((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ?
|
||||
checkAsyncFunctionReturnType(node) : // Async function
|
||||
getTypeFromTypeNode(node.type)); // AsyncGenerator function, Generator function, or normal function
|
||||
getTypeFromTypeNode(returnTypeNode)); // AsyncGenerator function, Generator function, or normal function
|
||||
|
||||
if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function
|
||||
// return is not necessary in the body of generators
|
||||
@ -16792,7 +16784,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (node.body) {
|
||||
if (!node.type) {
|
||||
if (!returnTypeNode) {
|
||||
// There are some checks that are only performed in getReturnTypeFromBody, that may produce errors
|
||||
// we need. An example is the noImplicitAny errors resulting from widening the return expression
|
||||
// of a function. Because checking of function expression bodies is deferred, there was never an
|
||||
@ -17578,8 +17570,9 @@ namespace ts {
|
||||
// There is no point in doing an assignability check if the function
|
||||
// has no explicit return type because the return type is directly computed
|
||||
// from the yield expressions.
|
||||
if (func.type) {
|
||||
const signatureElementType = getIteratedTypeOfGenerator(getTypeFromTypeNode(func.type), (functionFlags & FunctionFlags.Async) !== 0) || anyType;
|
||||
const returnType = getEffectiveReturnTypeNode(func);
|
||||
if (returnType) {
|
||||
const signatureElementType = getIteratedTypeOfGenerator(getTypeFromTypeNode(returnType), (functionFlags & FunctionFlags.Async) !== 0) || anyType;
|
||||
if (nodeIsYieldStar) {
|
||||
checkTypeAssignableTo(
|
||||
functionFlags & FunctionFlags.Async
|
||||
@ -18107,13 +18100,15 @@ namespace ts {
|
||||
|
||||
forEach(node.parameters, checkParameter);
|
||||
|
||||
// TODO(rbuckton): Should we start checking JSDoc types?
|
||||
if (node.type) {
|
||||
checkSourceElement(node.type);
|
||||
}
|
||||
|
||||
if (produceDiagnostics) {
|
||||
checkCollisionWithArgumentsInGeneratedCode(node);
|
||||
if (noImplicitAny && !node.type) {
|
||||
const returnTypeNode = getEffectiveReturnTypeNode(node);
|
||||
if (noImplicitAny && !returnTypeNode) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ConstructSignature:
|
||||
error(node, Diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type);
|
||||
@ -18124,12 +18119,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type) {
|
||||
if (returnTypeNode) {
|
||||
const functionFlags = getFunctionFlags(<FunctionDeclaration>node);
|
||||
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Generator)) === FunctionFlags.Generator) {
|
||||
const returnType = getTypeFromTypeNode(node.type);
|
||||
const returnType = getTypeFromTypeNode(returnTypeNode);
|
||||
if (returnType === voidType) {
|
||||
error(node.type, Diagnostics.A_generator_cannot_have_a_void_type_annotation);
|
||||
error(returnTypeNode, Diagnostics.A_generator_cannot_have_a_void_type_annotation);
|
||||
}
|
||||
else {
|
||||
const generatorElementType = getIteratedTypeOfGenerator(returnType, (functionFlags & FunctionFlags.Async) !== 0) || anyType;
|
||||
@ -18143,7 +18138,7 @@ namespace ts {
|
||||
// interface BadGenerator extends Iterable<number>, Iterator<string> { }
|
||||
// function* g(): BadGenerator { } // Iterable and Iterator have different types!
|
||||
//
|
||||
checkTypeAssignableTo(iterableIteratorInstantiation, returnType, node.type);
|
||||
checkTypeAssignableTo(iterableIteratorInstantiation, returnType, returnTypeNode);
|
||||
}
|
||||
}
|
||||
else if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) {
|
||||
@ -19155,7 +19150,8 @@ namespace ts {
|
||||
// then<U>(...): Promise<U>;
|
||||
// }
|
||||
//
|
||||
const returnType = getTypeFromTypeNode(node.type);
|
||||
const returnTypeNode = getEffectiveReturnTypeNode(node);
|
||||
const returnType = getTypeFromTypeNode(returnTypeNode);
|
||||
|
||||
if (languageVersion >= ScriptTarget.ES2015) {
|
||||
if (returnType === unknownType) {
|
||||
@ -19165,21 +19161,21 @@ namespace ts {
|
||||
if (globalPromiseType !== emptyGenericType && !isReferenceToType(returnType, globalPromiseType)) {
|
||||
// The promise type was not a valid type reference to the global promise type, so we
|
||||
// report an error and return the unknown type.
|
||||
error(node.type, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type);
|
||||
error(returnTypeNode, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type);
|
||||
return unknownType;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Always mark the type node as referenced if it points to a value
|
||||
markTypeNodeAsReferenced(node.type);
|
||||
markTypeNodeAsReferenced(returnTypeNode);
|
||||
|
||||
if (returnType === unknownType) {
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
const promiseConstructorName = getEntityNameFromTypeNode(node.type);
|
||||
const promiseConstructorName = getEntityNameFromTypeNode(returnTypeNode);
|
||||
if (promiseConstructorName === undefined) {
|
||||
error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, typeToString(returnType));
|
||||
error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, typeToString(returnType));
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
@ -19187,10 +19183,10 @@ namespace ts {
|
||||
const promiseConstructorType = promiseConstructorSymbol ? getTypeOfSymbol(promiseConstructorSymbol) : unknownType;
|
||||
if (promiseConstructorType === unknownType) {
|
||||
if (promiseConstructorName.kind === SyntaxKind.Identifier && promiseConstructorName.text === "Promise" && getTargetType(returnType) === getGlobalPromiseType(/*reportErrors*/ false)) {
|
||||
error(node.type, Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option);
|
||||
error(returnTypeNode, Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option);
|
||||
}
|
||||
else {
|
||||
error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName));
|
||||
error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName));
|
||||
}
|
||||
return unknownType;
|
||||
}
|
||||
@ -19199,11 +19195,11 @@ namespace ts {
|
||||
if (globalPromiseConstructorLikeType === emptyObjectType) {
|
||||
// If we couldn't resolve the global PromiseConstructorLike type we cannot verify
|
||||
// compatibility with __awaiter.
|
||||
error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName));
|
||||
error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName));
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type,
|
||||
if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, returnTypeNode,
|
||||
Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value)) {
|
||||
return unknownType;
|
||||
}
|
||||
@ -19348,7 +19344,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode {
|
||||
return node.dotDotDotToken ? getRestParameterElementType(node.type) : node.type;
|
||||
const typeNode = getEffectiveTypeAnnotationNode(node);
|
||||
return isRestParameter(node) ? getRestParameterElementType(typeNode) : typeNode;
|
||||
}
|
||||
|
||||
/** Check the decorators of a node */
|
||||
@ -19394,14 +19391,15 @@ namespace ts {
|
||||
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
|
||||
}
|
||||
|
||||
markDecoratorMedataDataTypeNodeAsReferenced((<FunctionLikeDeclaration>node).type);
|
||||
markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveReturnTypeNode(<FunctionLikeDeclaration>node));
|
||||
break;
|
||||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(<ParameterDeclaration>node));
|
||||
markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveTypeAnnotationNode(<ParameterDeclaration>node));
|
||||
break;
|
||||
|
||||
case SyntaxKind.Parameter:
|
||||
markDecoratorMedataDataTypeNodeAsReferenced((<PropertyDeclaration>node).type);
|
||||
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(<ParameterDeclaration>node));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -19466,14 +19464,15 @@ namespace ts {
|
||||
|
||||
checkSourceElement(node.body);
|
||||
|
||||
const returnTypeNode = getEffectiveReturnTypeNode(node);
|
||||
if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function
|
||||
const returnOrPromisedType = node.type && (functionFlags & FunctionFlags.Async
|
||||
const returnOrPromisedType = returnTypeNode && (functionFlags & FunctionFlags.Async
|
||||
? checkAsyncFunctionReturnType(node) // Async function
|
||||
: getTypeFromTypeNode(node.type)); // normal function
|
||||
: getTypeFromTypeNode(returnTypeNode)); // normal function
|
||||
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType);
|
||||
}
|
||||
|
||||
if (produceDiagnostics && !node.type) {
|
||||
if (produceDiagnostics && !returnTypeNode) {
|
||||
// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
|
||||
// in an ambient context
|
||||
if (noImplicitAny && nodeIsMissing(node.body) && !isPrivateWithinAmbient(node)) {
|
||||
@ -20603,7 +20602,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isGetAccessorWithAnnotatedSetAccessor(node: FunctionLikeDeclaration) {
|
||||
return !!(node.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind<SetAccessorDeclaration>(node.symbol, SyntaxKind.SetAccessor)));
|
||||
return node.kind === SyntaxKind.GetAccessor
|
||||
&& getEffectiveSetAccessorTypeAnnotationNode(getDeclarationOfKind<SetAccessorDeclaration>(node.symbol, SyntaxKind.SetAccessor)) !== undefined;
|
||||
}
|
||||
|
||||
function isUnwrappedReturnTypeVoidOrAny(func: FunctionLikeDeclaration, returnType: Type): boolean {
|
||||
@ -20647,7 +20647,7 @@ namespace ts {
|
||||
error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
|
||||
}
|
||||
}
|
||||
else if (func.type || isGetAccessorWithAnnotatedSetAccessor(func)) {
|
||||
else if (getEffectiveReturnTypeNode(func) || isGetAccessorWithAnnotatedSetAccessor(func)) {
|
||||
if (functionFlags & FunctionFlags.Async) { // Async function
|
||||
const promisedType = getPromisedTypeOfPromise(returnType);
|
||||
const awaitedType = checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
|
||||
|
||||
@ -1570,6 +1570,11 @@ namespace ts {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag;
|
||||
}
|
||||
|
||||
export function getJSDocReturnType(node: Node): JSDocType {
|
||||
const returnTag = getJSDocReturnTag(node);
|
||||
return returnTag && returnTag.typeExpression && returnTag.typeExpression.type;
|
||||
}
|
||||
|
||||
export function getJSDocTemplateTag(node: Node): JSDocTemplateTag {
|
||||
return getFirstJSDocTag(node, SyntaxKind.JSDocTemplateTag) as JSDocTemplateTag;
|
||||
}
|
||||
@ -2615,14 +2620,19 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
/** Get the type annotaion for the value parameter. */
|
||||
export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration): TypeNode {
|
||||
function getSetAccessorValueParameter(accessor: SetAccessorDeclaration): ParameterDeclaration | undefined {
|
||||
if (accessor && accessor.parameters.length > 0) {
|
||||
const hasThis = accessor.parameters.length === 2 && parameterIsThisKeyword(accessor.parameters[0]);
|
||||
return accessor.parameters[hasThis ? 1 : 0].type;
|
||||
return accessor.parameters[hasThis ? 1 : 0];
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the type annotation for the value parameter. */
|
||||
export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration): TypeNode {
|
||||
const parameter = getSetAccessorValueParameter(accessor);
|
||||
return parameter && parameter.type;
|
||||
}
|
||||
|
||||
export function getThisParameter(signature: SignatureDeclaration): ParameterDeclaration | undefined {
|
||||
if (signature.parameters.length) {
|
||||
const thisParameter = signature.parameters[0];
|
||||
@ -2701,6 +2711,41 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective type annotation of a variable, parameter, or property. If the node was
|
||||
* parsed in a JavaScript file, gets the type annotation from JSDoc.
|
||||
*/
|
||||
export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration): TypeNode {
|
||||
if (node.type) {
|
||||
return node.type;
|
||||
}
|
||||
if (node.flags & NodeFlags.JavaScriptFile) {
|
||||
return getJSDocType(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective return type annotation of a signature. If the node was parsed in a
|
||||
* JavaScript file, gets the return type annotation from JSDoc.
|
||||
*/
|
||||
export function getEffectiveReturnTypeNode(node: SignatureDeclaration): TypeNode {
|
||||
if (node.type) {
|
||||
return node.type;
|
||||
}
|
||||
if (node.flags & NodeFlags.JavaScriptFile) {
|
||||
return getJSDocReturnType(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective type annotation of the value parameter of a set accessor. If the node
|
||||
* was parsed in a JavaScript file, gets the type annotation from JSDoc.
|
||||
*/
|
||||
export function getEffectiveSetAccessorTypeAnnotationNode(node: SetAccessorDeclaration): TypeNode {
|
||||
const parameter = getSetAccessorValueParameter(node);
|
||||
return parameter && getEffectiveTypeAnnotationNode(parameter);
|
||||
}
|
||||
|
||||
export function emitNewLineBeforeLeadingComments(lineMap: number[], writer: EmitTextWriter, node: TextRange, leadingComments: CommentRange[]) {
|
||||
emitNewLineBeforeLeadingCommentsOfPosition(lineMap, writer, node.pos, leadingComments);
|
||||
}
|
||||
|
||||
25
tests/baselines/reference/checkJsdocReturnTag2.errors.txt
Normal file
25
tests/baselines/reference/checkJsdocReturnTag2.errors.txt
Normal file
@ -0,0 +1,25 @@
|
||||
tests/cases/conformance/jsdoc/returns.js(6,5): error TS2322: Type '5' is not assignable to type 'string'.
|
||||
tests/cases/conformance/jsdoc/returns.js(13,5): error TS2322: Type 'true | 5' is not assignable to type 'string | number'.
|
||||
Type 'true' is not assignable to type 'string | number'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsdoc/returns.js (2 errors) ====
|
||||
// @ts-check
|
||||
/**
|
||||
* @returns {string} This comment is not currently exposed
|
||||
*/
|
||||
function f() {
|
||||
return 5;
|
||||
~~~~~~~~~
|
||||
!!! error TS2322: Type '5' is not assignable to type 'string'.
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | number} This comment is not currently exposed
|
||||
*/
|
||||
function f1() {
|
||||
return 5 || true;
|
||||
~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2322: Type 'true | 5' is not assignable to type 'string | number'.
|
||||
!!! error TS2322: Type 'true' is not assignable to type 'string | number'.
|
||||
}
|
||||
@ -61,10 +61,10 @@ x(1);
|
||||
/** @type {function (number)} */
|
||||
const x1 = (a) => a + 1;
|
||||
>x1 : (arg0: number) => any
|
||||
>(a) => a + 1 : (a: any) => any
|
||||
>a : any
|
||||
>a + 1 : any
|
||||
>a : any
|
||||
>(a) => a + 1 : (a: number) => number
|
||||
>a : number
|
||||
>a + 1 : number
|
||||
>a : number
|
||||
>1 : 1
|
||||
|
||||
x1(0);
|
||||
@ -75,10 +75,10 @@ x1(0);
|
||||
/** @type {function (number): number} */
|
||||
const x2 = (a) => a + 1;
|
||||
>x2 : (arg0: number) => number
|
||||
>(a) => a + 1 : (a: any) => any
|
||||
>a : any
|
||||
>a + 1 : any
|
||||
>a : any
|
||||
>(a) => a + 1 : (a: number) => number
|
||||
>a : number
|
||||
>a + 1 : number
|
||||
>a : number
|
||||
>1 : 1
|
||||
|
||||
x2(0);
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
tests/cases/conformance/jsdoc/0.js(3,5): error TS2322: Type 'true' is not assignable to type 'string'.
|
||||
tests/cases/conformance/jsdoc/0.js(6,5): error TS2322: Type '"hello"' is not assignable to type 'number'.
|
||||
tests/cases/conformance/jsdoc/0.js(10,4): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'number'.
|
||||
tests/cases/conformance/jsdoc/0.js(13,7): error TS2451: Cannot redeclare block-scoped variable 'x2'.
|
||||
tests/cases/conformance/jsdoc/0.js(17,1): error TS2322: Type 'number' is not assignable to type 'string'.
|
||||
tests/cases/conformance/jsdoc/0.js(20,7): error TS2451: Cannot redeclare block-scoped variable 'x2'.
|
||||
tests/cases/conformance/jsdoc/0.js(20,21): error TS2339: Property 'concat' does not exist on type 'number'.
|
||||
tests/cases/conformance/jsdoc/0.js(24,7): error TS2322: Type '(a: number) => number' is not assignable to type '(arg0: number) => string'.
|
||||
Type 'number' is not assignable to type 'string'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsdoc/0.js (6 errors) ====
|
||||
@ -26,8 +27,6 @@ tests/cases/conformance/jsdoc/0.js(20,7): error TS2451: Cannot redeclare block-s
|
||||
|
||||
/** @type {function (number): number} */
|
||||
const x2 = (a) => a + 1;
|
||||
~~
|
||||
!!! error TS2451: Cannot redeclare block-scoped variable 'x2'.
|
||||
|
||||
/** @type {string} */
|
||||
var a;
|
||||
@ -36,7 +35,14 @@ tests/cases/conformance/jsdoc/0.js(20,7): error TS2451: Cannot redeclare block-s
|
||||
!!! error TS2322: Type 'number' is not assignable to type 'string'.
|
||||
|
||||
/** @type {function (number): number} */
|
||||
const x2 = (a) => a.concat("hi");
|
||||
const x3 = (a) => a.concat("hi");
|
||||
~~~~~~
|
||||
!!! error TS2339: Property 'concat' does not exist on type 'number'.
|
||||
x3(0);
|
||||
|
||||
/** @type {function (number): string} */
|
||||
const x4 = (a) => a + 1;
|
||||
~~
|
||||
!!! error TS2451: Cannot redeclare block-scoped variable 'x2'.
|
||||
x2(0);
|
||||
!!! error TS2322: Type '(a: number) => number' is not assignable to type '(arg0: number) => string'.
|
||||
!!! error TS2322: Type 'number' is not assignable to type 'string'.
|
||||
x4(0);
|
||||
@ -18,8 +18,12 @@ var a;
|
||||
a = x2(0);
|
||||
|
||||
/** @type {function (number): number} */
|
||||
const x2 = (a) => a.concat("hi");
|
||||
x2(0);
|
||||
const x3 = (a) => a.concat("hi");
|
||||
x3(0);
|
||||
|
||||
/** @type {function (number): string} */
|
||||
const x4 = (a) => a + 1;
|
||||
x4(0);
|
||||
|
||||
//// [0.js]
|
||||
// @ts-check
|
||||
@ -36,5 +40,8 @@ var x2 = function (a) { return a + 1; };
|
||||
var a;
|
||||
a = x2(0);
|
||||
/** @type {function (number): number} */
|
||||
var x2 = function (a) { return a.concat("hi"); };
|
||||
x2(0);
|
||||
var x3 = function (a) { return a.concat("hi"); };
|
||||
x3(0);
|
||||
/** @type {function (number): string} */
|
||||
var x4 = function (a) { return a + 1; };
|
||||
x4(0);
|
||||
|
||||
48
tests/baselines/reference/contextualTypeFromJSDoc.symbols
Normal file
48
tests/baselines/reference/contextualTypeFromJSDoc.symbols
Normal file
@ -0,0 +1,48 @@
|
||||
=== tests/cases/conformance/types/contextualTypes/jsdoc/index.js ===
|
||||
/** @type {Array<[string, {x?:number, y?:number}]>} */
|
||||
const arr = [
|
||||
>arr : Symbol(arr, Decl(index.js, 1, 5))
|
||||
|
||||
['a', { x: 1 }],
|
||||
>x : Symbol(x, Decl(index.js, 2, 11))
|
||||
|
||||
['b', { y: 2 }]
|
||||
>y : Symbol(y, Decl(index.js, 3, 11))
|
||||
|
||||
];
|
||||
|
||||
/** @return {Array<[string, {x?:number, y?:number}]>} */
|
||||
function f() {
|
||||
>f : Symbol(f, Decl(index.js, 4, 2))
|
||||
|
||||
return [
|
||||
['a', { x: 1 }],
|
||||
>x : Symbol(x, Decl(index.js, 9, 15))
|
||||
|
||||
['b', { y: 2 }]
|
||||
>y : Symbol(y, Decl(index.js, 10, 15))
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
class C {
|
||||
>C : Symbol(C, Decl(index.js, 12, 1))
|
||||
|
||||
/** @param {Array<[string, {x?:number, y?:number}]>} value */
|
||||
set x(value) { }
|
||||
>x : Symbol(C.x, Decl(index.js, 14, 9), Decl(index.js, 16, 20))
|
||||
>value : Symbol(value, Decl(index.js, 16, 10))
|
||||
|
||||
get x() {
|
||||
>x : Symbol(C.x, Decl(index.js, 14, 9), Decl(index.js, 16, 20))
|
||||
|
||||
return [
|
||||
['a', { x: 1 }],
|
||||
>x : Symbol(x, Decl(index.js, 19, 19))
|
||||
|
||||
['b', { y: 2 }]
|
||||
>y : Symbol(y, Decl(index.js, 20, 19))
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
77
tests/baselines/reference/contextualTypeFromJSDoc.types
Normal file
77
tests/baselines/reference/contextualTypeFromJSDoc.types
Normal file
@ -0,0 +1,77 @@
|
||||
=== tests/cases/conformance/types/contextualTypes/jsdoc/index.js ===
|
||||
/** @type {Array<[string, {x?:number, y?:number}]>} */
|
||||
const arr = [
|
||||
>arr : [string, { x?: number; y?: number; }][]
|
||||
>[ ['a', { x: 1 }], ['b', { y: 2 }]] : ([string, { x: number; }] | [string, { y: number; }])[]
|
||||
|
||||
['a', { x: 1 }],
|
||||
>['a', { x: 1 }] : [string, { x: number; }]
|
||||
>'a' : "a"
|
||||
>{ x: 1 } : { x: number; }
|
||||
>x : number
|
||||
>1 : 1
|
||||
|
||||
['b', { y: 2 }]
|
||||
>['b', { y: 2 }] : [string, { y: number; }]
|
||||
>'b' : "b"
|
||||
>{ y: 2 } : { y: number; }
|
||||
>y : number
|
||||
>2 : 2
|
||||
|
||||
];
|
||||
|
||||
/** @return {Array<[string, {x?:number, y?:number}]>} */
|
||||
function f() {
|
||||
>f : () => [string, { x?: number; y?: number; }][]
|
||||
|
||||
return [
|
||||
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ([string, { x: number; }] | [string, { y: number; }])[]
|
||||
|
||||
['a', { x: 1 }],
|
||||
>['a', { x: 1 }] : [string, { x: number; }]
|
||||
>'a' : "a"
|
||||
>{ x: 1 } : { x: number; }
|
||||
>x : number
|
||||
>1 : 1
|
||||
|
||||
['b', { y: 2 }]
|
||||
>['b', { y: 2 }] : [string, { y: number; }]
|
||||
>'b' : "b"
|
||||
>{ y: 2 } : { y: number; }
|
||||
>y : number
|
||||
>2 : 2
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
class C {
|
||||
>C : C
|
||||
|
||||
/** @param {Array<[string, {x?:number, y?:number}]>} value */
|
||||
set x(value) { }
|
||||
>x : [string, { x?: number; y?: number; }][]
|
||||
>value : [string, { x?: number; y?: number; }][]
|
||||
|
||||
get x() {
|
||||
>x : [string, { x?: number; y?: number; }][]
|
||||
|
||||
return [
|
||||
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ([string, { x: number; }] | [string, { y: number; }])[]
|
||||
|
||||
['a', { x: 1 }],
|
||||
>['a', { x: 1 }] : [string, { x: number; }]
|
||||
>'a' : "a"
|
||||
>{ x: 1 } : { x: number; }
|
||||
>x : number
|
||||
>1 : 1
|
||||
|
||||
['b', { y: 2 }]
|
||||
>['b', { y: 2 }] : [string, { y: number; }]
|
||||
>'b' : "b"
|
||||
>{ y: 2 } : { y: number; }
|
||||
>y : number
|
||||
>2 : 2
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -21,5 +21,9 @@ var a;
|
||||
a = x2(0);
|
||||
|
||||
/** @type {function (number): number} */
|
||||
const x2 = (a) => a.concat("hi");
|
||||
x2(0);
|
||||
const x3 = (a) => a.concat("hi");
|
||||
x3(0);
|
||||
|
||||
/** @type {function (number): string} */
|
||||
const x4 = (a) => a + 1;
|
||||
x4(0);
|
||||
@ -0,0 +1,30 @@
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noEmit: true
|
||||
// @filename: index.js
|
||||
// @target: esnext
|
||||
|
||||
/** @type {Array<[string, {x?:number, y?:number}]>} */
|
||||
const arr = [
|
||||
['a', { x: 1 }],
|
||||
['b', { y: 2 }]
|
||||
];
|
||||
|
||||
/** @return {Array<[string, {x?:number, y?:number}]>} */
|
||||
function f() {
|
||||
return [
|
||||
['a', { x: 1 }],
|
||||
['b', { y: 2 }]
|
||||
];
|
||||
}
|
||||
|
||||
class C {
|
||||
/** @param {Array<[string, {x?:number, y?:number}]>} value */
|
||||
set x(value) { }
|
||||
get x() {
|
||||
return [
|
||||
['a', { x: 1 }],
|
||||
['b', { y: 2 }]
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user