Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSDoc: document generic type that works for grandchildren classes

Tags:

Documenting generic type works for direct inheritance. But when I have a inheritance chain, there is no way to make it work for the grandchildren class. Here is an example:

 * @property {string} color
 * @template {T}
 */
class MyColor {
  constructor() {
    this.color = 'unknown';
  }

  /**
   * @returns {T}
   */
  static makeColor() {
    return /**@type {T}*/ new this.prototype.constructor();
  }
}

/**
 * @extends MyColor<Red>
 * @template {T}
 */
class Red extends MyColor {
  constructor() {
    super();
    this.color = 'red';
  }
}

/**
 * @extends Red<DarkRed>
 */
class DarkRed extends Red {
  constructor() {
    super();
    this.level = 2;
  }

  darker() {
    this.level += 1;
  }
}

const circle = DarkRed.makeColor();

DarkRed.makeColor only recognizes the return as Red, but not DarkRed. Is there a way to make it work with @template? Or is there any other way to make it work?

I am using WebStorm as the IDE.

like image 891
Comtaler Avatar asked Jun 28 '18 17:06

Comtaler


People also ask

Can you use JsDoc with TypeScript?

You can use most JSDoc type syntax and any TypeScript syntax, from the most basic like string to the most advanced, like conditional types.

Why should I use JsDoc?

JsDoc is a great tool for documenting code and providing type-safety in your JavaScript project without any additional configuration. Learn what JsDoc is, how it works, the benefits it provides, and how to use it in your project.


1 Answers

From https://github.com/google/closure-compiler/wiki/Generic-Types#inheritance-of-generic-types, @extends MyColor<Red> 'fixes' the template type instead of propagating it to inheriting type. For example, in

/**
 * @constructor
 * @template T
 */
var A = function() { };

/** @param {T} t */
A.prototype.method = function(t) {  };

/**
 * @constructor
 * @extends {A<string>}
 */
var B = function() {  };

/**
 * @constructor
 *
 * @extends {B<number>}
 */
var C = function() {  };


var cc =new C();
var bb = new B();

var bm = bb.method("hello");
var cm = cc.method(1);

cc.method(1) will result in TYPE_MISMATCH: actual parameter 1 of A.prototype.method does not match formal parameter found : number required: string

You can try changing your code to

/**
* @property {string} color
 * @template {T}
 */
class MyColor {
  constructor() {
    this.color = 'unknown';
  }

  /**
   * @returns {T}
   */
  static makeColor() {
    return /**@type {T}*/ new this.prototype.constructor();
  }
}

/**
 * @extends MyColor<T>
 * @template {T}
 */
class Red extends MyColor {
  constructor() {
    super();
    this.color = 'red';
  }
}

const circle1 = Red.makeColor();

/**
 * @extends Red<DarkRed>
 *
 */
class DarkRed extends Red {
  constructor() {
    super();
    this.level = 2;
  }

  darker() {
    this.level += 1;
  }
}

const circle = DarkRed.makeColor();

another possible solution is using @return {this} instead of @template (works since 2018.2):

class MyColor {
  constructor() {
    this.color = 'unknown';
  }

  /**
   * @return {this}
   */
  static makeColor() {
    return  new this.prototype.constructor();
  }
}


class Red extends MyColor {
  constructor() {
    super();
    this.color = 'red';
  }
}

class DarkRed extends Red {
  constructor() {
    super();
    this.level = 2;
  }

  darker() {
    this.level += 1;
  }
}
like image 128
lena Avatar answered Oct 11 '22 10:10

lena