# valid-types
* [Options](#user-content-valid-types-options)
* [`allowEmptyNamepaths`](#user-content-valid-types-options-allowemptynamepaths)
* [Context and settings](#user-content-valid-types-context-and-settings)
* [Failing examples](#user-content-valid-types-failing-examples)
* [Passing examples](#user-content-valid-types-passing-examples)
Requires all types/namepaths to be valid JSDoc, Closure compiler, or
TypeScript types (configured by `settings.jsdoc.mode`).
Note that what determines a valid type is handled by
our type parsing engine, [jsdoc-type-pratt-parser](https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser),
using [`settings.jsdoc.mode`](#user-content-eslint-plugin-jsdoc-settings-mode) to
determine whether to use jsdoc-type-pratt-parser's "permissive" parsing or
the stricter "jsdoc", "typescript", "closure" modes.
The following tags have their "type" portions (the segment within brackets)
checked (though those portions may sometimes be confined to namepaths,
e.g., `@modifies`):
1. Tags with required types: `@type`, `@implements`
1. Tags with required types in Closure or TypeScript: `@this`,
`@define` (Closure only)
1. Tags with optional types: `@enum`, `@member` (`@var`), `@typedef`,
`@augments` (or `@extends`), `@class` (or `@constructor`), `@constant`
(or `@const`), `@module` (module paths are not planned for TypeScript),
`@namespace`, `@throws`, `@exception`, `@yields` (or `@yield`),
`@modifies` (undocumented jsdoc); `@param` (`@arg`, `@argument`),
`@property` (`@prop`), and `@returns` (`@return`) also fall into this
category, but while this rule will check their type validity, we leave
the requiring of the type portion to the rules `require-param-type`,
`require-property-type`, and `require-returns-type`, respectively.
1. Tags with types that are available optionally in Closure: `@export`,
`@package`, `@private`, `@protected`, `@public`, `@static`;
`@template` (TypeScript also)
1. Tags with optional types that may take free text instead: `@throws`
The following tags have their name/namepath portion (the non-whitespace
text after the tag name) checked:
1. Name(path)-defining tags requiring namepath: `@event`, `@callback`,
`@exports` (JSDoc only),
`@external`, `@host`, `@name`, `@typedef` (JSDoc only), and `@template`
(TypeScript/Closure only); `@param` (`@arg`, `@argument`) and `@property`
(`@prop`) also fall into this category, but while this rule will check
their namepath validity, we leave the requiring of the name portion
to the rules `require-param-name` and `require-property-name`,
respectively.
1. Name(path)-defining tags (which may have value without namepath or their
namepath can be expressed elsewhere on the block):
`@class`, `@constructor`, `@constant`, `@const`, `@function`, `@func`,
`@method`, `@interface` (non-Closure only), `@member`, `@var`,
`@mixin`, `@namespace`, `@module` (module paths are not planned for
TypeScript)
1. Name(path)-pointing tags requiring namepath: `@alias`, `@augments`,
`@extends` (JSDoc only), `@lends`, `@memberof`, `@memberof!`, `@mixes`, `@requires`, `@this`
(JSDoc only)
1. Name(path)-pointing tags (which may have value without namepath or their
namepath can be expressed elsewhere on the block): `@listens`, `@fires`,
`@emits`.
1. Name(path)-pointing tags which may have free text or a namepath: `@see`
1. Name(path)-pointing tags (multiple names in one): `@borrows`
...with the following applying to the above sets:
- Expect tags in set 1-4 to have a valid namepath if present
- Prevent sets 2 and 4 from being empty by setting `allowEmptyNamepaths` to
`false` as these tags might have some indicative value without a path
or may allow a name expressed elsewhere on the block (but sets 1 and 3 will
always fail if empty)
- For the special case of set 6, i.e.,
`@borrows as `,
check that both namepaths are present and valid and ensure there is an `as `
between them. In the case of ``, it can be preceded by
one of the name path operators, `#`, `.`, or `~`.
- For the special case of `@memberof` and `@memberof!` (part of set 3), as
per the [specification](https://jsdoc.app/tags-memberof.html), they also
allow `#`, `.`, or `~` at the end (which is not allowed at the end of
normal paths).
If you define your own tags, `settings.jsdoc.structuredTags` will allow
these custom tags to be checked, with the name portion of tags checked for
valid namepaths (based on the tag's `name` value), their type portions checked
for valid types (based on the tag's `type` value), and either portion checked
for presence (based on `false` `name` or `type` values or their `required`
value). See the setting for more details.
## Options
A single options object has the following properties.
### allowEmptyNamepaths
Set to `false` to bulk disallow
empty name paths with namepath groups 2 and 4 (these might often be
expected to have an accompanying name path, though they have some
indicative value without one; these may also allow names to be defined
in another manner elsewhere in the block); you can use
`settings.jsdoc.structuredTags` with the `required` key set to "name" if you
wish to require name paths on a tag-by-tag basis. Defaults to `true`.
## Context and settings
|||
|---|---|
|Context|everywhere|
|Tags|For name only unless otherwise stated: `alias`, `augments`, `borrows`, `callback`, `class` (for name and type), `constant` (for name and type), `enum` (for type), `event`, `external`, `fires`, `function`, `implements` (for type), `interface`, `lends`, `listens`, `member` (for name and type), `memberof`, `memberof!`, `mixes`, `mixin`, `modifies`, `module` (for name and type), `name`, `namespace` (for name and type), `param` (for name and type), `property` (for name and type), `returns` (for type), `see` (optionally for name), `this`, `throws` (for type), `type` (for type), `typedef` (for name and type), `yields` (for type)|
|Aliases|`extends`, `constructor`, `const`, `host`, `emits`, `func`, `method`, `var`, `arg`, `argument`, `prop`, `return`, `exception`, `yield`|
|Closure-only|For type only: `package`, `private`, `protected`, `public`, `static`|
|Recommended|true|
|Options|`allowEmptyNamepaths`|
|Settings|`mode`, `structuredTags`|
## Failing examples
The following patterns are considered problems:
````ts
/**
* @param {Array): !Array}
*/
parseArray = function(parser) {
return function(array) {
return array.map(parser);
};
};
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: Syntax error in namepath: T<~, R
/**
* @template T, R<~
* @param {function(!T): !R} parser
* @return {function(!Array): !Array}
*/
parseArray = function(parser) {
return function(array) {
return array.map(parser);
};
};
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: Syntax error in namepath: R<~
/**
* @template T, R<~
* @param {function(!T): !R} parser
* @return {function(!Array): !Array}
*/
parseArray = function(parser) {
return function(array) {
return array.map(parser);
};
};
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: Syntax error in namepath: R<~
/**
* @suppress
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: Tag @suppress must have a type in "closure" mode.
/**
* @suppress {visibility} sth
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: @suppress should not have a name in "closure" mode.
/**
* @suppress {visibility|blah}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: Syntax error in suppress type: blah
/**
* @param {Object[]} employees
* @param {string} employees[.name - The name of an employee.
*/
function quux () {}
// Message: Invalid name: unpaired brackets
/**
* @param {Object[]} employees
* @param {string} [] - The name of an employee.
*/
function quux () {}
// Message: Invalid name: empty name
/**
* @param {string} [name=] - The name of an employee.
*/
function quux () {}
// Message: Invalid name: empty default value
/**
* @param {string} [name==] - The name of an employee.
*/
function quux () {}
// Message: Invalid name: invalid default value syntax
/**
* @type {{message: string?}}
*/
function quux (items) {
}
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: Syntax error in type: JsdocTypeNullable
/**
* @type {[message: string?]}
*/
function quux (items) {
}
// Settings: {"jsdoc":{"mode":"typescript"}}
// Message: Syntax error in type: JsdocTypeNullable
/**
* An inline {@link} tag without content.
*/
// Message: Inline tag "link" missing content
/**
* An inline {@tutorial} tag without content.
*/
// Message: Inline tag "tutorial" missing content
/**
* @param {SomeType} aName An inline {@link} tag without content.
*/
// Message: Inline tag "link" missing content
/**
* With reserved word in type
* @param {Array} foo
*/
function quux() {
}
// Message: Syntax error in type: Array
````
## Passing examples
The following patterns are not considered problems:
````ts
/**
* @param {Array} foo
*/
function quux() {
}
/**
* @param {string} foo
*/
function quux() {
}
/**
* @param foo
*/
function quux() {
}
/**
* @borrows foo as bar
*/
function quux() {
}
/**
* @borrows foo as #bar
*/
function quux() {
}
/**
* @see foo%
*/
function quux() {
}
/**
* @alias module:namespace.SomeClass#event:ext_anevent
*/
function quux() {
}
/**
* @callback foo
*/
function quux() {
}
/**
* @callback
*/
function quux() {
}
// "jsdoc/valid-types": ["error"|"warn", {"allowEmptyNamepaths":true}]
/**
* @class
*/
function quux() {
}
/**
* @see {@link foo}
*/
function quux() {
}
// Settings: {"jsdoc":{"structuredTags":{"see":{"name":"namepath-referencing","required":["name"]}}}}
/**
*
* @fires module:namespace.SomeClass#event:ext_anevent
*/
function quux() {
}
/**
* @memberof module:namespace.SomeClass~
*/
function quux() {
}
/**
* @memberof! module:namespace.SomeClass.
*/
function quux() {
}
/**
*
*/
function quux() {
}
/**
* @aCustomTag
*/
function quux() {
}
/**
* @constant {string}
*/
const FOO = 'foo';
/**
* @constant {string} FOO
*/
const FOO = 'foo';
/**
* @extends Foo
*/
class Bar {};
/**
* @extends Foo
*/
class Bar {};
/**
* @extends {Foo}
*/
class Bar {};
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @typedef {number | string} UserDefinedType
*/
/**
* @typedef {number | string}
*/
let UserDefinedGCCType;
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @modifies {foo | bar}
*/
function quux (foo, bar, baz) {}
/**
* @this {Navigator}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @export {SomeType}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @define {boolean}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @define
*/
function quux () {}
/**
* Foo function.
*
* @interface foo
*/
function foo(bar) {}
// Settings: {"jsdoc":{"mode":"typescript"}}
/**
* Foo function.
*
* @param {[number, string]} bar - The bar array.
*/
function foo(bar) {}
// Settings: {"jsdoc":{"mode":"typescript"}}
/**
* Foo function.
*
* @param {[number, string]} bar - The bar array.
*/
function foo(bar) {}
/**
* Foo function.
*
* @param {[number, string]} bar - The bar array.
*/
function foo(bar) {}
// Settings: {"jsdoc":{"mode":"permissive"}}
/**
* @typedef {SomeType}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
// "jsdoc/valid-types": ["error"|"warn", {"allowEmptyNamepaths":false}]
/**
* @private {SomeType}
*/
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @param
*/
function quux() {
}
// "jsdoc/valid-types": ["error"|"warn", {"allowEmptyNamepaths":false}]
/**
* @see
*/
function quux() {
}
// Settings: {"jsdoc":{"structuredTags":{"see":{"name":"namepath-referencing"}}}}
/**
* @template T, R
* @param {function(!T): !R} parser
* @return {function(!Array): !Array}
*/
parseArray = function(parser) {
return function(array) {
return array.map(parser);
};
};
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @template T, R<~
* @param {function(!T): !R} parser
* @return {function(!Array): !Array}
*/
parseArray = function(parser) {
return function(array) {
return array.map(parser);
};
};
// Settings: {"jsdoc":{"mode":"jsdoc"}}
/**
* @template {string} K - K must be a string or string literal
* @template {{ serious: string }} Seriousalizable - must have a serious property
* @param {K} key
* @param {Seriousalizable} object
*/
function seriousalize(key, object) {
// ????
}
// Settings: {"jsdoc":{"mode":"typescript"}}
/**
* @module foo/bar
*/
/**
* @module module:foo/bar
*/
/**
* @template invalid namepath,T Description
*/
function f() {}
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* Description of complicated type.
*
* @template T Description of the T type parameter.
* @template U - Like other tags, this can have an optional hyphen before the description.
* @template V,W More parameters
* @template W,X - Also with a hyphen
*/
type ComplicatedType = never
/** Multi-line typedef for an options object type.
*
* @typedef {{
* prop: number
* }} MyOptions
*/
/**
* @extends {SomeType}
*/
class quux {}
// Settings: {"jsdoc":{"mode":"typescript"}}
/**
* @suppress {visibility|underscore}
*/
function quux() {
}
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @param {string} id
* @param {Object} options
* @param {boolean} options.isSet
* @param {string} options.module
*/
function quux ( id, options ) {
}
/**
* Assign the project to a list of employees.
* @param {Object[]} employees - The employees who are responsible for the project.
* @param {string} employees[].name - The name of an employee.
* @param {string} employees[].department - The employee's department.
*/
function assign(employees) {
// ...
}
// "jsdoc/valid-types": ["error"|"warn", {"allowEmptyNamepaths":true}]
/**
* @param {typeof obj["level1"]["level2"]} foo
* @param {Parameters[0]} ghi
* @param {{[key: string]: string}} hjk
*/
function quux() {
}
// Settings: {"jsdoc":{"mode":"typescript"}}
/**
* @returns {Promise<{publicKey, privateKey}>} - The public and private key
*/
/**
* Some other {@inline} tag.
*/
/**
* @param {SomeType} aName An inline {@link text} tag with content.
*/
/**
* An inline {@link text} tag with content.
*/
/**
* @param typeof
* @param readonly
* @param import
* @param is
*/
function quux() {
}
/**
* @import { TestOne, TestTwo } from "./types"
*/
/**
* @returns {@link SomeType}
*/
/**
* @template {string} Selector
* @template {keyof GlobalEventHandlersEventMap} TEventType
* @template {Element} [TElement=import('typed-query-selector/parser').ParseSelector]
* @param {Selector} selector
* @param {TEventType} type
* @param {import('delegate-it').DelegateEventHandler} callback
* @param {Omit} [options]
* @returns {void}
*/
export function onGlobalEvent (selector, type, callback, options) {
delegate(document, selector, type, callback, options)
}
/**
* Even if added to `structuredTags` as in our recommended config,
* we don't want `valid-types` to report since
* `jsdoc/require-next-type` already does this.
* @next
*/
function a () {}
// Settings: {"jsdoc":{"structuredTags":{"next":{"required":["type"]}}}}
/**
* With reserved word in name
* @typedef {SomeType} import
*/
/**
* With reserved word in namepath
* @param {SomeType} import
*/
/**
* @param readonly
*/
/**
* @param {boolean} readonly
*/
/**
* @param {object} params
* @param {boolean} params.readonly
*/
/**
* An object interface
* @typedef {Object} FooBar
* @property {boolean} readonly
* @property {boolean} private
* @property {boolean} public
* @property {boolean} constant
*/
/**
* @param {object} props
* @param {string} props.is
*/
class Test {
/**
* @returns {this}
*/
method() { return this; }
}
/**
* @typedef {Object} module:src/core/Player~mediaFormat
*/
// Settings: {"jsdoc":{"mode":"jsdoc"}}
let SettingName = /** @type {const} */ ({
THEME: `theme`,
})
/**
* @typedef {Array} AnnotatedCharacter
* @property {string} 0 Character data
* @property {string[]} 1 Annotation hashses
*/
// Settings: {"jsdoc":{"mode":"jsdoc"}}
/** Some annoying set of bitmasks or something.
*
* @property {number} [BITMASK_VALUE_A=16] - blah blah
* @property {number} BITMASK_VALUE_B=32 - the other thing
*/
const MY_BITMASK_CONSTANT = {
BITMASK_VALUE_A: 1 << 4,
BITMASK_VALUE_B: somePrivateVariableHere
};
/**
* @template [T=Record]
*/
function quux () {}
````