Improve contextual types and return type checking

This commit is contained in:
Ron Buckton 2017-06-08 16:44:42 -07:00
parent 9e985c9619
commit ff1f33729b
9 changed files with 200 additions and 133 deletions

View File

@ -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.
@ -12752,8 +12743,9 @@ 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);
@ -12773,12 +12765,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);
}
}
}
}
@ -12832,10 +12825,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 ||
(isInJavaScriptFile(functionDecl) && getJSDocReturnType(functionDecl)) ||
functionDecl.kind === SyntaxKind.Constructor ||
functionDecl.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind<SetAccessorDeclaration>(functionDecl.symbol, SyntaxKind.SetAccessor), /*includeJSDocType*/ true)) {
if (functionDecl.kind === SyntaxKind.Constructor ||
getEffectiveReturnTypeNode(functionDecl) ||
isGetAccessorWithAnnotatedSetAccessor(functionDecl)) {
return getReturnTypeOfSignature(getSignatureFromDeclaration(functionDecl));
}
@ -16369,8 +16361,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));
}
}
}
@ -16385,14 +16378,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);
}
@ -16460,14 +16453,6 @@ namespace ts {
}
}
function getReturnTypeFromJSDocComment(func: SignatureDeclaration | FunctionDeclaration): Type {
const jsdocType = getJSDocReturnType(func);
if (jsdocType) {
return getTypeFromTypeNode(jsdocType);
}
return undefined;
}
function createPromiseType(promisedType: Type): Type {
// creates a `Promise<T>` type where `T` is the promisedType argument
const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true);
@ -16693,16 +16678,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) {
@ -16717,7 +16702,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);
}
}
@ -16757,7 +16742,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;
@ -16785,10 +16770,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
@ -16796,7 +16782,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
@ -17582,8 +17568,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
@ -18111,13 +18098,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);
@ -18128,12 +18117,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;
@ -18147,7 +18136,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) {
@ -19159,7 +19148,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) {
@ -19169,21 +19159,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;
}
@ -19191,10 +19181,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;
}
@ -19203,11 +19193,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;
}
@ -19352,7 +19342,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 */
@ -19398,14 +19389,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;
}
}
@ -19470,14 +19462,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)) {
@ -20607,7 +20600,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 {
@ -20651,7 +20645,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);

View File

@ -2620,20 +2620,19 @@ namespace ts {
});
}
/** Get the type annotation for the value parameter. */
export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration, includeJSDocType?: boolean): TypeNode {
function getSetAccessorValueParameter(accessor: SetAccessorDeclaration): ParameterDeclaration | undefined {
if (accessor && accessor.parameters.length > 0) {
const hasThis = accessor.parameters.length === 2 && parameterIsThisKeyword(accessor.parameters[0]);
const parameter = accessor.parameters[hasThis ? 1 : 0];
if (parameter.type) {
return parameter.type;
}
if (includeJSDocType && parameter.flags & NodeFlags.JavaScriptFile) {
return getJSDocType(parameter);
}
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];
@ -2712,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);
}

View 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'.
}

View File

@ -1,13 +1,13 @@
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 (7 errors) ====
==== tests/cases/conformance/jsdoc/0.js (6 errors) ====
// @ts-check
/** @type {String} */
var S = true;
@ -27,8 +27,6 @@ tests/cases/conformance/jsdoc/0.js(20,21): error TS2339: Property 'concat' does
/** @type {function (number): number} */
const x2 = (a) => a + 1;
~~
!!! error TS2451: Cannot redeclare block-scoped variable 'x2'.
/** @type {string} */
var a;
@ -37,9 +35,14 @@ tests/cases/conformance/jsdoc/0.js(20,21): error TS2339: Property 'concat' does
!!! error TS2322: Type 'number' is not assignable to type 'string'.
/** @type {function (number): number} */
const x2 = (a) => a.concat("hi");
~~
!!! error TS2451: Cannot redeclare block-scoped variable 'x2'.
const x3 = (a) => a.concat("hi");
~~~~~~
!!! error TS2339: Property 'concat' does not exist on type 'number'.
x2(0);
x3(0);
/** @type {function (number): string} */
const x4 = (a) => a + 1;
~~
!!! 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);

View File

@ -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);

View File

@ -11,7 +11,7 @@ const arr = [
];
/** @return {function(): Array<[string, {x?:number, y?:number}]>} */
/** @return {Array<[string, {x?:number, y?:number}]>} */
function f() {
>f : Symbol(f, Decl(index.js, 4, 2))
@ -28,13 +28,13 @@ function f() {
class C {
>C : Symbol(C, Decl(index.js, 12, 1))
/** @param {function(): Array<[string, {x?:number, y?:number}]>} value */
/** @param {Array<[string, {x?:number, y?:number}]>} value */
set x(value) { }
>x : Symbol(C.x, Decl(index.js, 14, 9))
>x : Symbol(C.x, Decl(index.js, 14, 9), Decl(index.js, 16, 20))
>value : Symbol(value, Decl(index.js, 16, 10))
get () {
>get : Symbol(C.get, Decl(index.js, 16, 20))
get x() {
>x : Symbol(C.x, Decl(index.js, 14, 9), Decl(index.js, 16, 20))
return [
['a', { x: 1 }],

View File

@ -20,24 +20,24 @@ const arr = [
];
/** @return {function(): Array<[string, {x?:number, y?:number}]>} */
/** @return {Array<[string, {x?:number, y?:number}]>} */
function f() {
>f : () => () => [string, { x?: number; y?: number; }][]
>f : () => [string, { x?: number; y?: number; }][]
return [
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ((string | { [x: string]: any; x: number; })[] | (string | { [x: string]: any; y: number; })[])[]
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ([string, { x: number; }] | [string, { y: number; }])[]
['a', { x: 1 }],
>['a', { x: 1 }] : (string | { [x: string]: any; x: number; })[]
>['a', { x: 1 }] : [string, { x: number; }]
>'a' : "a"
>{ x: 1 } : { [x: string]: any; x: number; }
>{ x: 1 } : { x: number; }
>x : number
>1 : 1
['b', { y: 2 }]
>['b', { y: 2 }] : (string | { [x: string]: any; y: number; })[]
>['b', { y: 2 }] : [string, { y: number; }]
>'b' : "b"
>{ y: 2 } : { [x: string]: any; y: number; }
>{ y: 2 } : { y: number; }
>y : number
>2 : 2
@ -47,28 +47,28 @@ function f() {
class C {
>C : C
/** @param {function(): Array<[string, {x?:number, y?:number}]>} value */
/** @param {Array<[string, {x?:number, y?:number}]>} value */
set x(value) { }
>x : any
>value : () => [string, { x?: number; y?: number; }][]
>x : [string, { x?: number; y?: number; }][]
>value : [string, { x?: number; y?: number; }][]
get () {
>get : () => ((string | { [x: string]: any; x: number; })[] | (string | { [x: string]: any; y: number; })[])[]
get x() {
>x : [string, { x?: number; y?: number; }][]
return [
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ((string | { [x: string]: any; x: number; })[] | (string | { [x: string]: any; y: number; })[])[]
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ([string, { x: number; }] | [string, { y: number; }])[]
['a', { x: 1 }],
>['a', { x: 1 }] : (string | { [x: string]: any; x: number; })[]
>['a', { x: 1 }] : [string, { x: number; }]
>'a' : "a"
>{ x: 1 } : { [x: string]: any; x: number; }
>{ x: 1 } : { x: number; }
>x : number
>1 : 1
['b', { y: 2 }]
>['b', { y: 2 }] : (string | { [x: string]: any; y: number; })[]
>['b', { y: 2 }] : [string, { y: number; }]
>'b' : "b"
>{ y: 2 } : { [x: string]: any; y: number; }
>{ y: 2 } : { y: number; }
>y : number
>2 : 2

View File

@ -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);

View File

@ -10,7 +10,7 @@ const arr = [
['b', { y: 2 }]
];
/** @return {function(): Array<[string, {x?:number, y?:number}]>} */
/** @return {Array<[string, {x?:number, y?:number}]>} */
function f() {
return [
['a', { x: 1 }],
@ -19,9 +19,9 @@ function f() {
}
class C {
/** @param {function(): Array<[string, {x?:number, y?:number}]>} value */
/** @param {Array<[string, {x?:number, y?:number}]>} value */
set x(value) { }
get () {
get x() {
return [
['a', { x: 1 }],
['b', { y: 2 }]