@@ -422,6 +422,7 @@ namespace ts {
422
422
423
423
let deferredNodes: Node[];
424
424
let deferredUnusedIdentifierNodes: Node[];
425
+ const seenDeferredUnusedIdentifiers = createMap<true>(); // For assertion that we don't defer the same identifier twice
425
426
426
427
let flowLoopStart = 0;
427
428
let flowLoopCount = 0;
@@ -2051,6 +2052,7 @@ namespace ts {
2051
2052
}
2052
2053
if (namespace.valueDeclaration &&
2053
2054
isVariableDeclaration(namespace.valueDeclaration) &&
2055
+ namespace.valueDeclaration.initializer &&
2054
2056
isCommonJsRequire(namespace.valueDeclaration.initializer)) {
2055
2057
const moduleName = (namespace.valueDeclaration.initializer as CallExpression).arguments[0] as StringLiteral;
2056
2058
const moduleSym = resolveExternalModuleName(moduleName, moduleName);
@@ -4165,7 +4167,10 @@ namespace ts {
4165
4167
return getTypeForBindingElement(<BindingElement>declaration);
4166
4168
}
4167
4169
4168
- const isOptional = !isBindingElement(declaration) && !isVariableDeclaration(declaration) && !!declaration.questionToken && includeOptionality;
4170
+ const isOptional = includeOptionality && (
4171
+ isInJavaScriptFile(declaration) && isParameter(declaration) && getJSDocParameterTags(declaration).some(tag => tag.isBracketed)
4172
+ || !isBindingElement(declaration) && !isVariableDeclaration(declaration) && !!declaration.questionToken);
4173
+
4169
4174
// Use type from type annotation if one is present
4170
4175
const declaredType = tryGetTypeFromEffectiveTypeNode(declaration);
4171
4176
if (declaredType) {
@@ -6569,23 +6574,10 @@ namespace ts {
6569
6574
}
6570
6575
6571
6576
function isJSDocOptionalParameter(node: ParameterDeclaration) {
6572
- if (isInJavaScriptFile(node)) {
6573
- if (node.type && node.type.kind === SyntaxKind.JSDocOptionalType) {
6574
- return true;
6575
- }
6576
- const paramTags = getJSDocParameterTags(node);
6577
- if (paramTags) {
6578
- for (const paramTag of paramTags) {
6579
- if (paramTag.isBracketed) {
6580
- return true;
6581
- }
6582
-
6583
- if (paramTag.typeExpression) {
6584
- return paramTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType;
6585
- }
6586
- }
6587
- }
6588
- }
6577
+ return isInJavaScriptFile(node) && (
6578
+ node.type && node.type.kind === SyntaxKind.JSDocOptionalType
6579
+ || getJSDocParameterTags(node).some(({ isBracketed, typeExpression }) =>
6580
+ isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType));
6589
6581
}
6590
6582
6591
6583
function tryFindAmbientModule(moduleName: string, withAugmentations: boolean) {
@@ -6761,17 +6753,20 @@ namespace ts {
6761
6753
return links.resolvedSignature;
6762
6754
}
6763
6755
6756
+ /**
6757
+ * A JS function gets a synthetic rest parameter if it references `arguments` AND:
6758
+ * 1. It has no parameters but at least one `@param` with a type that starts with `...`
6759
+ * OR
6760
+ * 2. It has at least one parameter, and the last parameter has a matching `@param` with a type that starts with `...`
6761
+ */
6764
6762
function maybeAddJsSyntheticRestParameter(declaration: SignatureDeclaration, parameters: Symbol[]): boolean {
6765
- // JS functions get a free rest parameter if:
6766
- // a) The last parameter has `...` preceding its type
6767
- // b) It references `arguments` somewhere
6763
+ if (!containsArgumentsReference(declaration)) {
6764
+ return false;
6765
+ }
6768
6766
const lastParam = lastOrUndefined(declaration.parameters);
6769
- const lastParamTags = lastParam && getJSDocParameterTags(lastParam);
6767
+ const lastParamTags = lastParam ? getJSDocParameterTags(lastParam) : getJSDocTags(declaration).filter(isJSDocParameterTag );
6770
6768
const lastParamVariadicType = firstDefined(lastParamTags, p =>
6771
6769
p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined);
6772
- if (!lastParamVariadicType && !containsArgumentsReference(declaration)) {
6773
- return false;
6774
- }
6775
6770
6776
6771
const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String);
6777
6772
syntheticArgsSymbol.type = lastParamVariadicType ? createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)) : anyArrayType;
@@ -8668,9 +8663,10 @@ namespace ts {
8668
8663
return getTypeFromIntersectionTypeNode(<IntersectionTypeNode>node);
8669
8664
case SyntaxKind.JSDocNullableType:
8670
8665
return getTypeFromJSDocNullableTypeNode(<JSDocNullableType>node);
8666
+ case SyntaxKind.JSDocOptionalType:
8667
+ return addOptionality(getTypeFromTypeNode((node as JSDocOptionalType).type));
8671
8668
case SyntaxKind.ParenthesizedType:
8672
8669
case SyntaxKind.JSDocNonNullableType:
8673
- case SyntaxKind.JSDocOptionalType:
8674
8670
case SyntaxKind.JSDocTypeExpression:
8675
8671
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
8676
8672
case SyntaxKind.JSDocVariadicType:
@@ -18673,7 +18669,6 @@ namespace ts {
18673
18669
18674
18670
// The identityMapper object is used to indicate that function expressions are wildcards
18675
18671
if (checkMode === CheckMode.SkipContextSensitive && isContextSensitive(node)) {
18676
- checkNodeDeferred(node);
18677
18672
return anyFunctionType;
18678
18673
}
18679
18674
@@ -21448,9 +21443,24 @@ namespace ts {
21448
21443
function checkJSDocParameterTag(node: JSDocParameterTag) {
21449
21444
checkSourceElement(node.typeExpression);
21450
21445
if (!getParameterSymbolFromJSDoc(node)) {
21451
- error(node.name,
21452
- Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name,
21453
- idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name));
21446
+ const decl = getHostSignatureFromJSDoc(node);
21447
+ // don't issue an error for invalid hosts -- just functions --
21448
+ // and give a better error message when the host function mentions `arguments`
21449
+ // but the tag doesn't have an array type
21450
+ if (decl) {
21451
+ if (!containsArgumentsReference(decl)) {
21452
+ error(node.name,
21453
+ Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name,
21454
+ idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name));
21455
+ }
21456
+ else if (findLast(getJSDocTags(decl), isJSDocParameterTag) === node &&
21457
+ node.typeExpression && node.typeExpression.type &&
21458
+ !isArrayType(getTypeFromTypeNode(node.typeExpression.type))) {
21459
+ error(node.name,
21460
+ Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name_It_would_match_arguments_if_it_had_an_array_type,
21461
+ idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name));
21462
+ }
21463
+ }
21454
21464
}
21455
21465
}
21456
21466
@@ -21461,7 +21471,7 @@ namespace ts {
21461
21471
return;
21462
21472
}
21463
21473
21464
- const augmentsTags = getAllJSDocTagsOfKind (classLike, SyntaxKind.JSDocAugmentsTag );
21474
+ const augmentsTags = getJSDocTags (classLike).filter(isJSDocAugmentsTag );
21465
21475
Debug.assert(augmentsTags.length > 0);
21466
21476
if (augmentsTags.length > 1) {
21467
21477
error(augmentsTags[1], Diagnostics.Class_declarations_cannot_have_more_than_one_augments_or_extends_tag);
@@ -21563,6 +21573,8 @@ namespace ts {
21563
21573
21564
21574
function registerForUnusedIdentifiersCheck(node: Node) {
21565
21575
if (deferredUnusedIdentifierNodes) {
21576
+ // TODO: GH#22580
21577
+ // Debug.assert(addToSeen(seenDeferredUnusedIdentifiers, getNodeId(node)), "Deferring unused identifier check twice");
21566
21578
deferredUnusedIdentifierNodes.push(node);
21567
21579
}
21568
21580
}
@@ -24477,18 +24489,19 @@ namespace ts {
24477
24489
const paramTag = parent.parent;
24478
24490
if (isJSDocTypeExpression(parent) && isJSDocParameterTag(paramTag)) {
24479
24491
// Else we will add a diagnostic, see `checkJSDocVariadicType`.
24480
- const param = getParameterSymbolFromJSDoc(paramTag);
24481
- if (param) {
24482
- const host = getHostSignatureFromJSDoc(paramTag);
24492
+ const host = getHostSignatureFromJSDoc(paramTag);
24493
+ if (host) {
24483
24494
/*
24484
- Only return an array type if the corresponding parameter is marked as a rest parameter.
24495
+ Only return an array type if the corresponding parameter is marked as a rest parameter, or if there are no parameters .
24485
24496
So in the following situation we will not create an array type:
24486
24497
/** @param {...number} a * /
24487
24498
function f(a) {}
24488
24499
Because `a` will just be of type `number | undefined`. A synthetic `...args` will also be added, which *will* get an array type.
24489
24500
*/
24490
- const lastParamDeclaration = host && last(host.parameters);
24491
- if (lastParamDeclaration.symbol === param && isRestParameter(lastParamDeclaration)) {
24501
+ const lastParamDeclaration = lastOrUndefined(host.parameters);
24502
+ const symbol = getParameterSymbolFromJSDoc(paramTag);
24503
+ if (!lastParamDeclaration ||
24504
+ symbol && lastParamDeclaration.symbol === symbol && isRestParameter(lastParamDeclaration)) {
24492
24505
return createArrayType(type);
24493
24506
}
24494
24507
}
@@ -24572,6 +24585,7 @@ namespace ts {
24572
24585
}
24573
24586
24574
24587
deferredNodes = undefined;
24588
+ seenDeferredUnusedIdentifiers.clear();
24575
24589
deferredUnusedIdentifierNodes = undefined;
24576
24590
24577
24591
if (isExternalOrCommonJsModule(node)) {
0 commit comments