diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 128e6410cb432..fc07d5838c924 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8847,9 +8847,11 @@ namespace ts { Debug.assertIsDefined(symbol.valueDeclaration); const declaration = symbol.valueDeclaration; if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) { - const decl = declaration as VariableDeclaration; - if (!decl.type) return anyType; - const type = getTypeOfNode(decl.type); + const typeNode = getEffectiveTypeAnnotationNode(declaration); + if (typeNode === undefined) { + return anyType; + } + const type = getTypeOfNode(typeNode); // an errorType will make `checkTryStatement` issue an error return isTypeAny(type) || type === unknownType ? type : errorType; } @@ -36044,10 +36046,11 @@ namespace ts { // Grammar checking if (catchClause.variableDeclaration) { const declaration = catchClause.variableDeclaration; - if (declaration.type) { + const typeNode = getEffectiveTypeAnnotationNode(getRootDeclaration(declaration)); + if (typeNode) { const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false); if (type && !(type.flags & TypeFlags.AnyOrUnknown)) { - grammarErrorOnFirstToken(declaration.type, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified); + grammarErrorOnFirstToken(typeNode, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified); } } else if (declaration.initializer) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 01f42d64917e6..3c1c118b5b33a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6279,6 +6279,7 @@ namespace ts { function parseVariableDeclaration(allowExclamation?: boolean): VariableDeclaration { const pos = getNodePos(); + const hasJSDoc = hasPrecedingJSDocComment(); const name = parseIdentifierOrPattern(Diagnostics.Private_identifiers_are_not_allowed_in_variable_declarations); let exclamationToken: ExclamationToken | undefined; if (allowExclamation && name.kind === SyntaxKind.Identifier && @@ -6288,7 +6289,7 @@ namespace ts { const type = parseTypeAnnotation(); const initializer = isInOrOfKeyword(token()) ? undefined : parseInitializer(); const node = factory.createVariableDeclaration(name, exclamationToken, type, initializer); - return finishNode(node, pos); + return withJSDoc(finishNode(node, pos), hasJSDoc); } function parseVariableDeclarationList(inForStatementInitializer: boolean): VariableDeclarationList { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5b6bd03eecfd8..c82072fe01587 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -874,6 +874,7 @@ namespace ts { | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration + | VariableDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration @@ -1237,7 +1238,7 @@ namespace ts { export type BindingName = Identifier | BindingPattern; - export interface VariableDeclaration extends NamedDeclaration { + export interface VariableDeclaration extends NamedDeclaration, JSDocContainer { readonly kind: SyntaxKind.VariableDeclaration; readonly parent: VariableDeclarationList | CatchClause; readonly name: BindingName; // Declared variable name diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 13289cc4595d8..e30462c20be82 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1204,7 +1204,8 @@ namespace ts { node.kind === SyntaxKind.TypeParameter || node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction || - node.kind === SyntaxKind.ParenthesizedExpression) ? + node.kind === SyntaxKind.ParenthesizedExpression || + node.kind === SyntaxKind.VariableDeclaration) ? concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) : getLeadingCommentRanges(text, node.pos); // True if the comment starts with '/**' but not if it is '/**/' diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index aa6a43193d819..09d18c29586c3 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -558,7 +558,7 @@ declare namespace ts { } export interface JSDocContainer { } - export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | EndOfFileToken; + export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | VariableDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | EndOfFileToken; export type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType; export type HasTypeArguments = CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement | JsxSelfClosingElement; export type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute; @@ -687,7 +687,7 @@ declare namespace ts { readonly kind: SyntaxKind.ConstructSignature; } export type BindingName = Identifier | BindingPattern; - export interface VariableDeclaration extends NamedDeclaration { + export interface VariableDeclaration extends NamedDeclaration, JSDocContainer { readonly kind: SyntaxKind.VariableDeclaration; readonly parent: VariableDeclarationList | CatchClause; readonly name: BindingName; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 775a7a4114400..c9e62e0c62797 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -558,7 +558,7 @@ declare namespace ts { } export interface JSDocContainer { } - export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | EndOfFileToken; + export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | VariableDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | NamespaceExportDeclaration | ExportAssignment | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | NamedTupleMember | EndOfFileToken; export type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType; export type HasTypeArguments = CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement | JsxSelfClosingElement; export type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute; @@ -687,7 +687,7 @@ declare namespace ts { readonly kind: SyntaxKind.ConstructSignature; } export type BindingName = Identifier | BindingPattern; - export interface VariableDeclaration extends NamedDeclaration { + export interface VariableDeclaration extends NamedDeclaration, JSDocContainer { readonly kind: SyntaxKind.VariableDeclaration; readonly parent: VariableDeclarationList | CatchClause; readonly name: BindingName; diff --git a/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.errors.txt b/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.errors.txt new file mode 100644 index 0000000000000..ee77d6e3decb9 --- /dev/null +++ b/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.errors.txt @@ -0,0 +1,75 @@ +tests/cases/conformance/jsdoc/foo.js(20,54): error TS2339: Property 'foo' does not exist on type 'unknown'. +tests/cases/conformance/jsdoc/foo.js(21,54): error TS2339: Property 'foo' does not exist on type 'unknown'. +tests/cases/conformance/jsdoc/foo.js(22,31): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. +tests/cases/conformance/jsdoc/foo.js(23,31): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. +tests/cases/conformance/jsdoc/foo.js(35,7): error TS2492: Cannot redeclare identifier 'err' in catch clause. +tests/cases/conformance/jsdoc/foo.js(48,31): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. +tests/cases/conformance/jsdoc/foo.js(49,31): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. + + +==== tests/cases/conformance/jsdoc/foo.js (7 errors) ==== + /** + * @typedef {any} Any + */ + + /** + * @typedef {unknown} Unknown + */ + + function fn() { + try { } catch (x) { } // should be OK + try { } catch (/** @type {any} */ err) { } // should be OK + try { } catch (/** @type {Any} */ err) { } // should be OK + try { } catch (/** @type {unknown} */ err) { } // should be OK + try { } catch (/** @type {Unknown} */ err) { } // should be OK + try { } catch (err) { err.foo; } // should be OK + try { } catch (/** @type {any} */ err) { err.foo; } // should be OK + try { } catch (/** @type {Any} */ err) { err.foo; } // should be OK + try { } catch (/** @type {unknown} */ err) { console.log(err); } // should be OK + try { } catch (/** @type {Unknown} */ err) { console.log(err); } // should be OK + try { } catch (/** @type {unknown} */ err) { err.foo; } // error in the body + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'unknown'. + try { } catch (/** @type {Unknown} */ err) { err.foo; } // error in the body + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'unknown'. + try { } catch (/** @type {Error} */ err) { } // error in the type + ~~~~~ +!!! error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. + try { } catch (/** @type {object} */ err) { } // error in the type + ~~~~~~ +!!! error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. + + try { console.log(); } + // @ts-ignore + catch (/** @type {number} */ err) { // e should not be a `number` + console.log(err.toLowerCase()); + } + + // minor bug: shows that the `catch` argument is skipped when checking scope + try { } + catch (err) { + /** @type {string} */ + let err; + ~~~ +!!! error TS2492: Cannot redeclare identifier 'err' in catch clause. + } + try { } + catch (err) { + /** @type {boolean} */ + var err; + } + + try { } catch ({ x }) { } // should be OK + try { } catch (/** @type {any} */ { x }) { x.foo; } // should be OK + try { } catch (/** @type {Any} */ { x }) { x.foo;} // should be OK + try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK + try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK + try { } catch (/** @type {Error} */ { x }) { } // error in the type + ~~~~~ +!!! error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. + try { } catch (/** @type {object} */ { x }) { } // error in the type + ~~~~~~ +!!! error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.js b/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.js new file mode 100644 index 0000000000000..ce948d9011690 --- /dev/null +++ b/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.js @@ -0,0 +1,144 @@ +//// [foo.js] +/** + * @typedef {any} Any + */ + +/** + * @typedef {unknown} Unknown + */ + +function fn() { + try { } catch (x) { } // should be OK + try { } catch (/** @type {any} */ err) { } // should be OK + try { } catch (/** @type {Any} */ err) { } // should be OK + try { } catch (/** @type {unknown} */ err) { } // should be OK + try { } catch (/** @type {Unknown} */ err) { } // should be OK + try { } catch (err) { err.foo; } // should be OK + try { } catch (/** @type {any} */ err) { err.foo; } // should be OK + try { } catch (/** @type {Any} */ err) { err.foo; } // should be OK + try { } catch (/** @type {unknown} */ err) { console.log(err); } // should be OK + try { } catch (/** @type {Unknown} */ err) { console.log(err); } // should be OK + try { } catch (/** @type {unknown} */ err) { err.foo; } // error in the body + try { } catch (/** @type {Unknown} */ err) { err.foo; } // error in the body + try { } catch (/** @type {Error} */ err) { } // error in the type + try { } catch (/** @type {object} */ err) { } // error in the type + + try { console.log(); } + // @ts-ignore + catch (/** @type {number} */ err) { // e should not be a `number` + console.log(err.toLowerCase()); + } + + // minor bug: shows that the `catch` argument is skipped when checking scope + try { } + catch (err) { + /** @type {string} */ + let err; + } + try { } + catch (err) { + /** @type {boolean} */ + var err; + } + + try { } catch ({ x }) { } // should be OK + try { } catch (/** @type {any} */ { x }) { x.foo; } // should be OK + try { } catch (/** @type {Any} */ { x }) { x.foo;} // should be OK + try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK + try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK + try { } catch (/** @type {Error} */ { x }) { } // error in the type + try { } catch (/** @type {object} */ { x }) { } // error in the type +} + + +//// [foo.js] +/** + * @typedef {any} Any + */ +/** + * @typedef {unknown} Unknown + */ +function fn() { + try { } + catch (x) { } // should be OK + try { } + catch ( /** @type {any} */err) { } // should be OK + try { } + catch ( /** @type {Any} */err) { } // should be OK + try { } + catch ( /** @type {unknown} */err) { } // should be OK + try { } + catch ( /** @type {Unknown} */err) { } // should be OK + try { } + catch (err) { + err.foo; + } // should be OK + try { } + catch ( /** @type {any} */err) { + err.foo; + } // should be OK + try { } + catch ( /** @type {Any} */err) { + err.foo; + } // should be OK + try { } + catch ( /** @type {unknown} */err) { + console.log(err); + } // should be OK + try { } + catch ( /** @type {Unknown} */err) { + console.log(err); + } // should be OK + try { } + catch ( /** @type {unknown} */err) { + err.foo; + } // error in the body + try { } + catch ( /** @type {Unknown} */err) { + err.foo; + } // error in the body + try { } + catch ( /** @type {Error} */err) { } // error in the type + try { } + catch ( /** @type {object} */err) { } // error in the type + try { + console.log(); + } + // @ts-ignore + catch ( /** @type {number} */err) { // e should not be a `number` + console.log(err.toLowerCase()); + } + // minor bug: shows that the `catch` argument is skipped when checking scope + try { } + catch (err) { + /** @type {string} */ + let err; + } + try { } + catch (err) { + /** @type {boolean} */ + var err; + } + try { } + catch ({ x }) { } // should be OK + try { } + catch ( /** @type {any} */{ x }) { + x.foo; + } // should be OK + try { } + catch ( /** @type {Any} */{ x }) { + x.foo; + } // should be OK + try { } + catch ( /** @type {unknown} */{ x }) { + console.log(x); + } // should be OK + try { } + catch ( /** @type {Unknown} */{ x }) { + console.log(x); + } // should be OK + try { } + catch ( /** @type {Error} */{ x }) { } // error in the type + try { } + catch ( /** @type {object} */{ x }) { } // error in the type +} diff --git a/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.symbols b/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.symbols new file mode 100644 index 0000000000000..5b722006126c2 --- /dev/null +++ b/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.symbols @@ -0,0 +1,133 @@ +=== tests/cases/conformance/jsdoc/foo.js === +/** + * @typedef {any} Any + */ + +/** + * @typedef {unknown} Unknown + */ + +function fn() { +>fn : Symbol(fn, Decl(foo.js, 0, 0)) + + try { } catch (x) { } // should be OK +>x : Symbol(x, Decl(foo.js, 9, 19)) + + try { } catch (/** @type {any} */ err) { } // should be OK +>err : Symbol(err, Decl(foo.js, 10, 19)) + + try { } catch (/** @type {Any} */ err) { } // should be OK +>err : Symbol(err, Decl(foo.js, 11, 19)) + + try { } catch (/** @type {unknown} */ err) { } // should be OK +>err : Symbol(err, Decl(foo.js, 12, 19)) + + try { } catch (/** @type {Unknown} */ err) { } // should be OK +>err : Symbol(err, Decl(foo.js, 13, 19)) + + try { } catch (err) { err.foo; } // should be OK +>err : Symbol(err, Decl(foo.js, 14, 19)) +>err : Symbol(err, Decl(foo.js, 14, 19)) + + try { } catch (/** @type {any} */ err) { err.foo; } // should be OK +>err : Symbol(err, Decl(foo.js, 15, 19)) +>err : Symbol(err, Decl(foo.js, 15, 19)) + + try { } catch (/** @type {Any} */ err) { err.foo; } // should be OK +>err : Symbol(err, Decl(foo.js, 16, 19)) +>err : Symbol(err, Decl(foo.js, 16, 19)) + + try { } catch (/** @type {unknown} */ err) { console.log(err); } // should be OK +>err : Symbol(err, Decl(foo.js, 17, 19)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>err : Symbol(err, Decl(foo.js, 17, 19)) + + try { } catch (/** @type {Unknown} */ err) { console.log(err); } // should be OK +>err : Symbol(err, Decl(foo.js, 18, 19)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>err : Symbol(err, Decl(foo.js, 18, 19)) + + try { } catch (/** @type {unknown} */ err) { err.foo; } // error in the body +>err : Symbol(err, Decl(foo.js, 19, 19)) +>err : Symbol(err, Decl(foo.js, 19, 19)) + + try { } catch (/** @type {Unknown} */ err) { err.foo; } // error in the body +>err : Symbol(err, Decl(foo.js, 20, 19)) +>err : Symbol(err, Decl(foo.js, 20, 19)) + + try { } catch (/** @type {Error} */ err) { } // error in the type +>err : Symbol(err, Decl(foo.js, 21, 19)) + + try { } catch (/** @type {object} */ err) { } // error in the type +>err : Symbol(err, Decl(foo.js, 22, 19)) + + try { console.log(); } +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + + // @ts-ignore + catch (/** @type {number} */ err) { // e should not be a `number` +>err : Symbol(err, Decl(foo.js, 26, 11)) + + console.log(err.toLowerCase()); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>err : Symbol(err, Decl(foo.js, 26, 11)) + } + + // minor bug: shows that the `catch` argument is skipped when checking scope + try { } + catch (err) { +>err : Symbol(err, Decl(foo.js, 32, 8)) + + /** @type {string} */ + let err; +>err : Symbol(err, Decl(foo.js, 34, 5)) + } + try { } + catch (err) { +>err : Symbol(err, Decl(foo.js, 37, 8)) + + /** @type {boolean} */ + var err; +>err : Symbol(err, Decl(foo.js, 39, 5)) + } + + try { } catch ({ x }) { } // should be OK +>x : Symbol(x, Decl(foo.js, 42, 20)) + + try { } catch (/** @type {any} */ { x }) { x.foo; } // should be OK +>x : Symbol(x, Decl(foo.js, 43, 39)) +>x : Symbol(x, Decl(foo.js, 43, 39)) + + try { } catch (/** @type {Any} */ { x }) { x.foo;} // should be OK +>x : Symbol(x, Decl(foo.js, 44, 39)) +>x : Symbol(x, Decl(foo.js, 44, 39)) + + try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK +>x : Symbol(x, Decl(foo.js, 45, 43)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>x : Symbol(x, Decl(foo.js, 45, 43)) + + try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK +>x : Symbol(x, Decl(foo.js, 46, 43)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>x : Symbol(x, Decl(foo.js, 46, 43)) + + try { } catch (/** @type {Error} */ { x }) { } // error in the type +>x : Symbol(x, Decl(foo.js, 47, 41)) + + try { } catch (/** @type {object} */ { x }) { } // error in the type +>x : Symbol(x, Decl(foo.js, 48, 42)) +} + diff --git a/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.types b/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.types new file mode 100644 index 0000000000000..f79e074df4cc2 --- /dev/null +++ b/tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.types @@ -0,0 +1,156 @@ +=== tests/cases/conformance/jsdoc/foo.js === +/** + * @typedef {any} Any + */ + +/** + * @typedef {unknown} Unknown + */ + +function fn() { +>fn : () => void + + try { } catch (x) { } // should be OK +>x : any + + try { } catch (/** @type {any} */ err) { } // should be OK +>err : any + + try { } catch (/** @type {Any} */ err) { } // should be OK +>err : any + + try { } catch (/** @type {unknown} */ err) { } // should be OK +>err : unknown + + try { } catch (/** @type {Unknown} */ err) { } // should be OK +>err : unknown + + try { } catch (err) { err.foo; } // should be OK +>err : any +>err.foo : any +>err : any +>foo : any + + try { } catch (/** @type {any} */ err) { err.foo; } // should be OK +>err : any +>err.foo : any +>err : any +>foo : any + + try { } catch (/** @type {Any} */ err) { err.foo; } // should be OK +>err : any +>err.foo : any +>err : any +>foo : any + + try { } catch (/** @type {unknown} */ err) { console.log(err); } // should be OK +>err : unknown +>console.log(err) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>err : unknown + + try { } catch (/** @type {Unknown} */ err) { console.log(err); } // should be OK +>err : unknown +>console.log(err) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>err : unknown + + try { } catch (/** @type {unknown} */ err) { err.foo; } // error in the body +>err : unknown +>err.foo : any +>err : unknown +>foo : any + + try { } catch (/** @type {Unknown} */ err) { err.foo; } // error in the body +>err : unknown +>err.foo : any +>err : unknown +>foo : any + + try { } catch (/** @type {Error} */ err) { } // error in the type +>err : any + + try { } catch (/** @type {object} */ err) { } // error in the type +>err : any + + try { console.log(); } +>console.log() : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void + + // @ts-ignore + catch (/** @type {number} */ err) { // e should not be a `number` +>err : any + + console.log(err.toLowerCase()); +>console.log(err.toLowerCase()) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>err.toLowerCase() : any +>err.toLowerCase : any +>err : any +>toLowerCase : any + } + + // minor bug: shows that the `catch` argument is skipped when checking scope + try { } + catch (err) { +>err : any + + /** @type {string} */ + let err; +>err : string + } + try { } + catch (err) { +>err : any + + /** @type {boolean} */ + var err; +>err : boolean + } + + try { } catch ({ x }) { } // should be OK +>x : any + + try { } catch (/** @type {any} */ { x }) { x.foo; } // should be OK +>x : any +>x.foo : any +>x : any +>foo : any + + try { } catch (/** @type {Any} */ { x }) { x.foo;} // should be OK +>x : any +>x.foo : any +>x : any +>foo : any + + try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK +>x : any +>console.log(x) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x : any + + try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK +>x : any +>console.log(x) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>x : any + + try { } catch (/** @type {Error} */ { x }) { } // error in the type +>x : any + + try { } catch (/** @type {object} */ { x }) { } // error in the type +>x : any +} + diff --git a/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.js b/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.js new file mode 100644 index 0000000000000..a896bbef4ab0f --- /dev/null +++ b/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.js @@ -0,0 +1,10 @@ +//// [foo.js] +/** @type {boolean} */ +var /** @type {string} */ x, + /** @type {number} */ y; + + +//// [foo.js] +/** @type {boolean} */ +var /** @type {string} */ x, +/** @type {number} */ y; diff --git a/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.symbols b/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.symbols new file mode 100644 index 0000000000000..e39fd9d551f50 --- /dev/null +++ b/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.symbols @@ -0,0 +1,8 @@ +=== tests/cases/conformance/jsdoc/foo.js === +/** @type {boolean} */ +var /** @type {string} */ x, +>x : Symbol(x, Decl(foo.js, 1, 3)) + + /** @type {number} */ y; +>y : Symbol(y, Decl(foo.js, 1, 28)) + diff --git a/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.types b/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.types new file mode 100644 index 0000000000000..e75a0f692fd25 --- /dev/null +++ b/tests/baselines/reference/jsdocVariableDeclarationWithTypeAnnotation.types @@ -0,0 +1,8 @@ +=== tests/cases/conformance/jsdoc/foo.js === +/** @type {boolean} */ +var /** @type {string} */ x, +>x : string + + /** @type {number} */ y; +>y : number + diff --git a/tests/cases/conformance/jsdoc/jsdocCatchClauseWithTypeAnnotation.ts b/tests/cases/conformance/jsdoc/jsdocCatchClauseWithTypeAnnotation.ts new file mode 100644 index 0000000000000..1b9982073f2ec --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocCatchClauseWithTypeAnnotation.ts @@ -0,0 +1,57 @@ +// @allowJs: true +// @checkJs: true +// @target: esnext +// @noImplicitAny: true +// @outDir: out +// @Filename: foo.js + +/** + * @typedef {any} Any + */ + +/** + * @typedef {unknown} Unknown + */ + +function fn() { + try { } catch (x) { } // should be OK + try { } catch (/** @type {any} */ err) { } // should be OK + try { } catch (/** @type {Any} */ err) { } // should be OK + try { } catch (/** @type {unknown} */ err) { } // should be OK + try { } catch (/** @type {Unknown} */ err) { } // should be OK + try { } catch (err) { err.foo; } // should be OK + try { } catch (/** @type {any} */ err) { err.foo; } // should be OK + try { } catch (/** @type {Any} */ err) { err.foo; } // should be OK + try { } catch (/** @type {unknown} */ err) { console.log(err); } // should be OK + try { } catch (/** @type {Unknown} */ err) { console.log(err); } // should be OK + try { } catch (/** @type {unknown} */ err) { err.foo; } // error in the body + try { } catch (/** @type {Unknown} */ err) { err.foo; } // error in the body + try { } catch (/** @type {Error} */ err) { } // error in the type + try { } catch (/** @type {object} */ err) { } // error in the type + + try { console.log(); } + // @ts-ignore + catch (/** @type {number} */ err) { // e should not be a `number` + console.log(err.toLowerCase()); + } + + // minor bug: shows that the `catch` argument is skipped when checking scope + try { } + catch (err) { + /** @type {string} */ + let err; + } + try { } + catch (err) { + /** @type {boolean} */ + var err; + } + + try { } catch ({ x }) { } // should be OK + try { } catch (/** @type {any} */ { x }) { x.foo; } // should be OK + try { } catch (/** @type {Any} */ { x }) { x.foo;} // should be OK + try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK + try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK + try { } catch (/** @type {Error} */ { x }) { } // error in the type + try { } catch (/** @type {object} */ { x }) { } // error in the type +} diff --git a/tests/cases/conformance/jsdoc/jsdocVariableDeclarationWithTypeAnnotation.ts b/tests/cases/conformance/jsdoc/jsdocVariableDeclarationWithTypeAnnotation.ts new file mode 100644 index 0000000000000..4a12859f41b41 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocVariableDeclarationWithTypeAnnotation.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @checkJs: true +// @target: esnext +// @noImplicitAny: true +// @outDir: out +// @Filename: foo.js + +/** @type {boolean} */ +var /** @type {string} */ x, + /** @type {number} */ y;