diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7ec1a0ae2b2..f66e48c0c81 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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 ((symbol.valueDeclaration).type) { + if (getEffectiveTypeAnnotationNode(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 = 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(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(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 = signature.parameters[i].valueDeclaration; - if (declaration.type) { - inferTypes((mapper).inferences, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i)); + const typeNode = getEffectiveTypeAnnotationNode(declaration); + if (typeNode) { + inferTypes((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 (!(parameter.valueDeclaration).type) { + if (!getEffectiveTypeAnnotationNode(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 (!(parameter.valueDeclaration).type) { + if (!getEffectiveTypeAnnotationNode(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` 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(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, Iterator { } // 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(...): Promise; // } // - 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((node).type); + markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveReturnTypeNode(node)); break; case SyntaxKind.PropertyDeclaration: - markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(node)); + markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveTypeAnnotationNode(node)); break; + case SyntaxKind.Parameter: - markDecoratorMedataDataTypeNodeAsReferenced((node).type); + markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(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(node.symbol, SyntaxKind.SetAccessor))); + return node.kind === SyntaxKind.GetAccessor + && getEffectiveSetAccessorTypeAnnotationNode(getDeclarationOfKind(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); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3f4c78c7376..611d419a363 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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); } diff --git a/tests/baselines/reference/checkJsdocReturnTag2.errors.txt b/tests/baselines/reference/checkJsdocReturnTag2.errors.txt new file mode 100644 index 00000000000..23a450c83ca --- /dev/null +++ b/tests/baselines/reference/checkJsdocReturnTag2.errors.txt @@ -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'. + } \ No newline at end of file diff --git a/tests/baselines/reference/checkJsdocTypeTag1.types b/tests/baselines/reference/checkJsdocTypeTag1.types index 9368640edcb..898cf58806a 100644 --- a/tests/baselines/reference/checkJsdocTypeTag1.types +++ b/tests/baselines/reference/checkJsdocTypeTag1.types @@ -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); diff --git a/tests/baselines/reference/checkJsdocTypeTag2.errors.txt b/tests/baselines/reference/checkJsdocTypeTag2.errors.txt index ca02c578a36..3590c10e278 100644 --- a/tests/baselines/reference/checkJsdocTypeTag2.errors.txt +++ b/tests/baselines/reference/checkJsdocTypeTag2.errors.txt @@ -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); \ No newline at end of file +!!! 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); \ No newline at end of file diff --git a/tests/baselines/reference/checkJsdocTypeTag2.js b/tests/baselines/reference/checkJsdocTypeTag2.js index 23b436e2031..0e550d3ba33 100644 --- a/tests/baselines/reference/checkJsdocTypeTag2.js +++ b/tests/baselines/reference/checkJsdocTypeTag2.js @@ -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); diff --git a/tests/baselines/reference/contextualTypeFromJSDoc.symbols b/tests/baselines/reference/contextualTypeFromJSDoc.symbols new file mode 100644 index 00000000000..5ebe1e01ca7 --- /dev/null +++ b/tests/baselines/reference/contextualTypeFromJSDoc.symbols @@ -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)) + + ]; + } +} diff --git a/tests/baselines/reference/contextualTypeFromJSDoc.types b/tests/baselines/reference/contextualTypeFromJSDoc.types new file mode 100644 index 00000000000..f1121935d38 --- /dev/null +++ b/tests/baselines/reference/contextualTypeFromJSDoc.types @@ -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 + + ]; + } +} diff --git a/tests/cases/conformance/jsdoc/checkJsdocTypeTag2.ts b/tests/cases/conformance/jsdoc/checkJsdocTypeTag2.ts index a7dffdb90a7..14c6fffd6d6 100644 --- a/tests/cases/conformance/jsdoc/checkJsdocTypeTag2.ts +++ b/tests/cases/conformance/jsdoc/checkJsdocTypeTag2.ts @@ -21,5 +21,9 @@ var a; a = x2(0); /** @type {function (number): number} */ -const x2 = (a) => a.concat("hi"); -x2(0); \ No newline at end of file +const x3 = (a) => a.concat("hi"); +x3(0); + +/** @type {function (number): string} */ +const x4 = (a) => a + 1; +x4(0); \ No newline at end of file diff --git a/tests/cases/conformance/types/contextualTypes/jsdoc/contextualTypeFromJSDoc.ts b/tests/cases/conformance/types/contextualTypes/jsdoc/contextualTypeFromJSDoc.ts new file mode 100644 index 00000000000..c9d7cc54d58 --- /dev/null +++ b/tests/cases/conformance/types/contextualTypes/jsdoc/contextualTypeFromJSDoc.ts @@ -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 }] + ]; + } +} \ No newline at end of file