Verify JS function is constructor

This commit is contained in:
Ron Buckton
2017-06-06 13:33:19 -07:00
parent 08e71641bf
commit 8cb5333791
5 changed files with 45 additions and 4 deletions

View File

@@ -15892,7 +15892,7 @@ namespace ts {
const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
if (callSignatures.length) {
const signature = resolveCall(node, callSignatures, candidatesOutArray);
if (!isInJavaScriptFile(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) {
if (!isJavaScriptConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) {
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
}
if (getThisTypeOfSignature(signature) === voidType) {
@@ -16119,6 +16119,26 @@ namespace ts {
return getNodeLinks(node).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(node);
}
/**
* Indicates whether a declaration can be treated as a constructor in a JavaScript
* file.
*/
function isJavaScriptConstructor(node: Declaration): boolean {
if (isInJavaScriptFile(node)) {
// If the node has a @class tag, treat it like a constructor.
if (getJSDocClassTag(node)) return true;
// If the symbol of the node has members, treat it like a constructor.
const symbol = isFunctionDeclaration(node) || isFunctionExpression(node) ? getSymbolOfNode(node) :
isVariableDeclaration(node) && isFunctionExpression(node.initializer) ? getSymbolOfNode(node.initializer) :
undefined;
return symbol && symbol.members !== undefined;
}
return false;
}
function getInferredClassType(symbol: Symbol) {
const links = getSymbolLinks(symbol);
if (!links.inferredClassType) {

View File

@@ -6533,6 +6533,10 @@ namespace ts {
case "augments":
tag = parseAugmentsTag(atToken, tagName);
break;
case "class":
case "constructor":
tag = parseClassTag(atToken, tagName);
break;
case "arg":
case "argument":
case "param":
@@ -6752,6 +6756,13 @@ namespace ts {
return finishNode(result);
}
function parseClassTag(atToken: AtToken, tagName: Identifier): JSDocClassTag {
const tag = <JSDocClassTag>createNode(SyntaxKind.JSDocClassTag, atToken.pos);
tag.atToken = atToken;
tag.tagName = tagName;
return finishNode(tag);
}
function parseTypedefTag(atToken: AtToken, tagName: Identifier): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();

View File

@@ -374,6 +374,7 @@ namespace ts {
JSDocComment,
JSDocTag,
JSDocAugmentsTag,
JSDocClassTag,
JSDocParameterTag,
JSDocReturnTag,
JSDocTypeTag,
@@ -2132,6 +2133,10 @@ namespace ts {
typeExpression: JSDocTypeExpression;
}
export interface JSDocClassTag extends JSDocTag {
kind: SyntaxKind.JSDocClassTag;
}
export interface JSDocTemplateTag extends JSDocTag {
kind: SyntaxKind.JSDocTemplateTag;
typeParameters: NodeArray<TypeParameterDeclaration>;

View File

@@ -1562,6 +1562,10 @@ namespace ts {
return getFirstJSDocTag(node, SyntaxKind.JSDocAugmentsTag) as JSDocAugmentsTag;
}
export function getJSDocClassTag(node: Node): JSDocClassTag {
return getFirstJSDocTag(node, SyntaxKind.JSDocClassTag) as JSDocClassTag;
}
export function getJSDocReturnTag(node: Node): JSDocReturnTag {
return getFirstJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag;
}