Type Checking in JavaScript files with JS Doc

JSDoc annotations to provide type information in JavaScript files

Velu S Gautam2 min readLearnings

You can type-check and report errors in JavaScript files using JSDoc annotations, checked by the TypeScript compiler (version 2.3 or later).

This lets you use JavaScript with types, without a compile step, and while keeping the original file format. 🌿

Below are the constructs TypeScript currently supports when using JSDoc annotations to add type information to JavaScript files.

TEXT
@type
@param (or @arg or @argument)
@return (or @returns)
@typedef
@callback
@template
@this
@extends (or @augments)
@enum

@type

The type tag references a type name. You can use any TypeScript type, and most JSDoc types too.

JAVASCRIPT
/** @type {string}  */
var value;

/** @type {number} */
var myNumber;

/** @type {HTMLElement} */
var myElement;

@type can be defined to accept a set of types, like only string | boolean

JAVASCRIPT
/** @type {string | boolean}  */
var value;

@type can also accept specific inner types, like an array of numbers or an object with a: string and b: number

JAVASCRIPT
/** @type {Array<number>} */
var numberArray;

/** @type {{ a: string, b: number }} */
var valueObject;

@type definitions for a function can look like this:

JAVASCRIPT
/** @type {function(string, boolean): number} Closure syntax */
var calc;

/** @type {function} */
var calc;

@type can also express any or unknown:

JAVASCRIPT
/**
 * @type {*} - can be 'any' type
 */
var value1;
/**
 * @type {?} - unknown type (same as 'any')
 */
var value2;

@type can point to a type defined in another file and then be imported:

JAVASCRIPT
/**
 * @typedef { import("./button.types").props } props
 */
var button;

@params and @returns

@param documents a function's arguments. It's similar to @type, but with a parameter name attached. @returns defines the annotated function's return type; you can also use @return.

JAVASCRIPT
/**
 * Calc function takes 4 arguments
 * @param {number}  num1 - A number param.
 * @param {number=} num2 - An optional param (Closure syntax)
 * @param {number} [num3] - Another optional param (JSDoc syntax).
 * @param {number} [num4="test"] - An optional param with a default value
 * @returns {number} This is the return result
 */
function calc(num1, num2, num3, num4){
  // TODO
}

@param can be nested, as shown below:

JAVASCRIPT
/**
 * calculation method
 * @param {Object} options - The shape is the same as SpecialType above
 * @param {string} options.prop1
 * @param {number} options.prop2
 * @param {number=} options.prop3 // optional parameter
 * @param {number} [options.prop4=42] //optional parameter with default value
 */
function calculation({prop1, prop2, prop3, prop4}) {
  return (options.prop3 || 1001) + options.prop4;
}

@typedef and @callbacks

@typedef lets you define complex types. You can do the same inline with @param.

JAVASCRIPT
/**
 * @typedef {object} SpecialType - creates a new type named 'SpecialType'
 * @property {string} prop1 - a string property of SpecialType
 * @property {number} prop2 - a number property of SpecialType
 * @property {number=} prop3 - a number property of SpecialType which is  optional
 * @property {number} [options.prop4=42] - a number property of SpecialType which is optional parameter with default value
 */

/** @type {SpecialType} */
function calculation({prop1, prop2, prop3, prop4}) {
  return (options.prop3 || 1001) + options.prop4;
}

@callback is similar to @typedef, but it defines a function type instead of an object type.

JAVASCRIPT
/**
 * @callback DoubleLength
 * @param {string} data
 * @param {number} [index]
 * @returns {boolean}
 */
/** @type {DoubleLength} */
const ok = data => !(data.length % 2);

The same callback can be defined with @typedef instead, as shown below:

JAVASCRIPT
/** @typedef {(data: string, index?: number) => boolean} DoubleLength */

@template

For generic type definitions, use @template. The example below ties the argument type to the return type:

JAVASCRIPT
/**
 * @template S
 * @param {S} p1 - A generic parameter that flows through to the return type
 * @return {S}
 */
function id(data){ return data }

@this

If you need to set the type of this explicitly, use the @this annotation.

JAVASCRIPT
/**
 * @this {HTMLElement}
 * @param {*} e
 */
function callback(e) {
    this.width = parseInt(e)
}

@extends

A class that extends an existing generic base class can be annotated with @extends.

JAVASCRIPT
/**
 * @template T
 * @extends {Map<T>}
 */
class HashMap extends Map {
  // ...
}

@enum

@enum defines an object literal whose members are all of a specified type.

JAVASCRIPT
/** @enum {number} */
const BankAccount = {
  CurrentBalance: 1000,
  SavingsBalance: 200,
  UnclearedBalance: 200,
}

That's all, folks. Now you can write your JavaScript with type safety. ⛑

Anyone using your typed code gets proper IDE tooltips showing what types to pass, and your IDE will catch type errors directly.

Cheers!!✌🏻

Comments