7.10 Type annotations
Type annotations are found on @param
, @return
, @this
, and @type
tags,and optionally on @const
, @export
, and any visibility tags. Typeannotations attached to JSDoc tags must always be enclosed in braces.
7.10.1 Nullability
The type system defines modifiers !
and ?
for non-null and nullable,respectively. These modifiers must precede the type.
Nullability modifiers have different requirements for different types, whichfall into two broad categories:
- Type annotations for primitives (
string
,number
,boolean
,symbol
,undefined
,null
) and literals ({function(…): …}
and{{foo:string…}}
) are always non-nullable by default. Use the?
modifier tomake it nullable, but omit the redundant!
. - Reference types (generally, anything in
UpperCamelCase
, includingsome.namespace.ReferenceType
) refer to a class, enum, record, or typedefdefined elsewhere. Since these types may or may not be nullable, it isimpossible to tell from the name alone whether it is nullable or not. Alwaysuse explicit?
and!
modifiers for these types to prevent ambiguity atuse sites.Bad:
const /** MyObject */ myObject = null; // Non-primitive types must be annotated.
const /** !number */ someNum = 5; // Primitives are non-nullable by default.
const /** number? */ someNullableNum = null; // ? should precede the type.
const /** !{foo: string, bar: number} */ record = ...; // Already non-nullable.
const /** MyTypeDef */ def = ...; // Not sure if MyTypeDef is nullable.
// Not sure if object (nullable), enum (non-nullable, unless otherwise
// specified), or typedef (depends on definition).
const /** SomeCamelCaseName */ n = ...;
Good:
const /** ?MyObject */ myObject = null;
const /** number */ someNum = 5;
const /** ?number */ someNullableNum = null;
const /** {foo: string, bar: number} */ record = ...;
const /** !MyTypeDef */ def = ...;
const /** ?SomeCamelCaseName */ n = ...;
7.10.2 Type Casts
In cases where the compiler doesn't accurately infer the type of an expression,and the assertion functions ingoog.assertscannot remedy it , it is possible totighten the type by adding a type annotation comment and enclosing theexpression in parentheses. Note that the parentheses are required.
/** @type {number} */ (x)
7.10.3 Template Parameter Types
Always specify template parameters. This way compiler can do a better job and itmakes it easier for readers to understand what code does.
Bad:
const /** !Object */ users = {};
const /** !Array */ books = [];
const /** !Promise */ response = ...;
Good:
const /** !Object<string, !User> */ users = {};
const /** !Array<string> */ books = [];
const /** !Promise<!Response> */ response = ...;
const /** !Promise<undefined> */ thisPromiseReturnsNothingButParameterIsStillUseful = ...;
const /** !Object<string, *> */ mapOfEverything = {};
Cases when template parameters should not be used:
Object
is used for type hierarchy and not as map-like structure.
7.10.4 Function type expressions
Terminology Note: function type expression refers to a type annotation forfunction types with the keyword function
in the annotation (see examplesbelow).
Where the function definition is given, do not use a function type expression.Specify parameter and return types with @param
and @return
, or with inlineannotations (see ??). This includesanonymous functions and functions defined and assigned to a const (where thefunction jsdoc appears above the whole assignment expression).
Function type expressions are needed, for example, inside @typedef
, @param
or @return
. Use it also for variables or properties of function type, if theyare not immediately initialized with the function definition.
/** @private {function(string): string} */
this.idGenerator_ = googFunctions.identity;
When using a function type expression, always specify the return typeexplicitly. Otherwise the default return type is unknown
(?
), which leads tostrange and unexpected behavior, and is rarely what is actually desired.
Bad - type error, but no warning given:
/** @param {function()} generateNumber */
function foo(generateNumber) {
const /** number */ x = generateNumber(); // No compile-time type error here.
}
foo(() => 'clearly not a number');
Good:
/**
* @param {function(): *} inputFunction1 Can return any type.
* @param {function(): undefined} inputFunction2 Definitely doesn't return
* anything.
* NOTE: the return type of `foo` itself is safely implied to be {undefined}.
*/
function foo(inputFunction1, inputFunction2) {...}
7.10.5 Whitespace
Within a type annotation, a single space or line break is required after eachcomma or colon. Additional line breaks may be inserted to improve readability oravoid exceeding the column limit. These breaks should be chosen and indentedfollowing the applicable guidelines (e.g. ?? and??). No other whitespace is allowed in typeannotations.
Good:
/** @type {function(string): number} */
/** @type {{foo: number, bar: number}} */
/** @type {number|string} */
/** @type {!Object<string, string>} */
/** @type {function(this: Object<string, string>, number): string} */
/**
* @type {function(
* !SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak,
* !OtherVeryLongTypedef): string}
*/
/**
* @type {!SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak|
* !OtherVeryLongTypedef}
*/
Bad:
// Only put a space after the colon
/** @type {function(string) : number} */
// Put spaces after colons and commas
/** @type {{foo:number,bar:number}} */
// No space in union types
/** @type {number | string} */