'Write JSdoc for generic function in typescript

I've distilled an essence of my problem with following codes:

full source

I have Base class, and Derived, Derived2:

class Base {
    static get type() {
        return 'Base';
    }
}

class Derived extends Base {
}

class Derived2 extends Base {
}

Now I have variable t, which could be an instance of Derived or Derived2. It can also be changed multiple times during runtime.

/** @type {Base} */
var t = new Derived();
//or
var t = new Derived2();

And I have a function which checks whether t is an instance of passed-class, and returns t if it's an instance of the passed class or undefined otherwise.

/**
 * @template {typeof Base} T
 * @param {T} cl
 * @returns {T}  /// <-- I can't figure out how to return an instance of T
 * @returns {instanceof T} /// it's hypothetical, but I need this..
 */
function checkTop( cl ) {
    if ( t instanceof cl ) {
        return t;
    }
    return undefined;
}

When I call checkTop( Derived ), its return type should be Derived. But with above jsdoc, its return type is 'typeof Derived'. But I want to make the return type just 'Derived'.

let d1 = checkTop( Derived ); // 'typeof Derived', but I want 'Derived' as return type

d1 is recognized as 'typeof Derived'

likewise, d2 is recognized as 'typeof Derived2'

let d2 = checkTop( Derived2 ); // 'typeof Derived2'.. but I want 'Derived2' as return type

d2 is recognized as 'typeof Derived2'

How can I specify the return type in JSDOC so that checkTop( Derived ); has return type as Derived, and checkTop( Derived2 )'s return type is 'Derived2'.

I tried following for the return type:

/**
 * @template {Base} B 
 * @template {typeof B} T
 * @param {T} cl
 * @returns {B}
 */
function checkTop( cl )

and

/**
 * @template {typeof Base} T
 * @param {T} cl
 * @returns {instanceof T}
 */
function checkTop( cl )

If it's not possible in JSDOC, but possible in typescript, that would be helpful also, but I prefer JSDOC solution.



Solution 1:[1]

Define the template as a type you need to return and the parameter as a constructor of that type

/**
* @template {Base} T
* @param {new T} cl
* @returns {T}
*/
function checkTop( cl ) {
    if ( t instanceof cl ) {
        return t;
    }
    return undefined;
}

The result will be:

function checkTop<T extends Base>(cl: new () => T): T

Solution 2:[2]

I may be misunderstanding the ask here, but I think you just need to return cl instead of returning t:

/**
 * @template {typeof Base} T
 * @param {T} cl
 * @returns {T}  /// <-- I can't figure out how o return an instance of T
 */
function checkTop( cl ) {
    if ( t instanceof cl ) {
        return cl;
    }
    return undefined;
}

Solution 3:[3]

You've asked for case if it's possible at least in a TypeScript. Yes it is. Just remove your JSDoc comment and you will have a suggestion you are looking for.

For clearance you can just make a comment which only says of what it returns. That's enough.

enter image description here enter image description here

You were just rewriting the correct TypeScript suggestions by your assertions (the programmer's opinion is considered to be more important that TypeScript's suggestion since the programmer knows what he's doing when specifying something explicitly).

Solution 4:[4]

So I read about jsdocs on internet and here is my conclusion I found the required result

See the screenshot and feel happy, issue resolved.

enter image description here

So what helps in the jsDocs

/**
 * @typedef {Object} 
 * @param {c1}
 */

here is the code snippet

class Base {
  static get type() {
      return 'Base';
  }
}

class Derived extends Base {
}

class Derived2 extends Base {
}

var t = new Derived();
var t1 = new Derived2();

/**
 * @typedef {Object} 
 * @param {c1}
 */
function checkTop( cl ) {
  if ( t instanceof cl ) {
      return t;
  }
  return undefined;
}

/**
 * @typedef {Object} 
 * @param {c1}
 */
function checkTop2( cl ) {
  if ( t1 instanceof cl ) {
      return t1;
  }
  return undefined;
}
var d1 = checkTop(Derived);
var d2 = checkTop2(Derived2);

Solution 5:[5]

Here is how I was able to accomplish this... The constructor type should be typed as {new() => T}

/**
 * @template {Base} T
 * @param {new() => T} cl
 * @returns {T}
 */
function checkTop( cl ) {
    if ( t instanceof cl ) {
        return t
    }
    
    // You should always return an instance
    // of cl unless the return type is {?T}
    return new cl()
}

Solution 6:[6]

You can try Typescript Generics to implement what you want to achieve and it's good to use Typescript feature instead of JSDOCS

Try following code and its working fine.

   class Base
   {
       static get type() {
           return 'Base';
       }
   }
  
   class Derived extends Base {}

   class Derived2 extends Base {}
  
   var t : Derived = new Derived();
   var t1 : Derived2= new Derived2();
  
   function checkTop <GT>(genericValue: GT){
       return genericValue;
   }

   var d1 = checkTop(t);
   var d2 = checkTop(t1);

enter image description here


OR

After spending some time learning TypeScript, I get to know we about type

declare type A = { id: string }
declare type B = { name: string }

function get<T>(obj: T): T {
    return obj;
}

var obj1: A = {id : "Jarvis"};
var obj2: B = {name : "Tony Stark"};

var objectTypeA = get(obj1);


var objectTypeB = get(obj2);

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Eloise Maun
Solution 3 Sergey
Solution 4 Dupinder Singh
Solution 5 Ray Perea
Solution 6