How jsdoc describes static class method which returns SAME CLASS variable

Viewed 393

This is very simple example of what I want to get. My question is about @returns tag. What should I write there?

class Base{
  /**
  * @returns {QUESTION: WHAT SHOUL BE HIRE??}
  */
  static method(){
    return new this()
  }
}

class Sub extends Base{
}

let base= Base.method() // IDE should understand that base is instance of Base
let sub= Sub.method() // IDE should understand that sub is instance of Sub
2 Answers

There is no "relative" type. You could fairly trivally modify the extended class;

/** @extends {Base} */
class Sub extends Base{
  /**  @returns {Sub} */
  static method(){
    return super.method();
  }
}

Or perhaps use a third type, an @interface that defines this method's existence

/** @interface */
class I {
  method() {}
}
/** @implements {I} */
class Base {
  /**  @returns {I} */
  static method(){
    return new this();
  }
}

/** 
 * @extends {Base}
 * @implements {I}
 */
class Sub {
  /**  @returns {I} */
  static method(){
    return new this();
  }
}

I know that this is a very old question and that in most circumstances projects needing a polymorphic this for static methods would now be using TypeScript with the workaround as listed here.

Several of our browser based projects are still using pure ES module JavaScript without any build step and introducing a build step for this use case seems over the top. On these projects we are also using Visual Studio Code's 'Implicit Project Config: Check JS' to enable type checking. Having to cast the return of every extended static method is a real pain!

The following workaround works well in Visual Studio Code for JavaScript with JSDoc:

enter image description here

/**
 * @template  T
 * @param {T} Class
 * @returns {{
 *   method: () => InstanceType<T>
 *  } & T}
 */
// @ts-ignore
const fixPolymorphicThis = Class => Class

class Base {
    static method() {
        return new this()
    }
}
const Sub = fixPolymorphicThis(class Sub extends Base { })

let base = Base.method() // IDE should understand that base is instance of Base
let sub = Sub.method() // IDE should understand that sub is instance of Sub

console.assert(sub instanceof Base)
console.assert(sub instanceof Sub)

Related