12

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.

Dupinder Singh
  • 7,175
  • 6
  • 37
  • 61
Vincent
  • 3,191
  • 3
  • 29
  • 35
  • [Cannot reproduce](https://www.w3schools.com/code/tryit.asp?filename=G5NQLVKHGH16). Console shows an object of type Derived for d1, and undefined for d2. Perhaps I'm misunderstanding something? –  Jul 04 '19 at 05:23
  • @Chipster I'm using vscode and it's recognized as 'typeof Derived2' for d2. I've added screenshots. The return type is T which extends {typeof Base} so it should at least have a 'typeof something'. But I want to remove the 'typeof' and recognize the return type as just an instance. – Vincent Jul 05 '19 at 00:49
  • 'typeof Derived1' returns {} and null for the 'typeof Derived2', checkTop() function will always return the type of the variable because you pass a class that is invoked all ready, you can return the 'cl' of Derived the class declared – bdalina Jul 06 '19 at 03:30
  • class Derived extends Base {} console.log(Derived); // returns a function(){...} console(new Derived()); ///returns object {} //becuase it was already invoked – bdalina Jul 06 '19 at 03:33
  • Maybe that way https://stackoverflow.com/questions/21002316/documenting-the-return-of-a-javascript-constructor-with-jsdoc because basically you are returning an object which is just an instance. Meanwhile, you want to show a returning type. The type is object. You can specify that it's an instance of an object in a comment. – Sergey Jul 08 '19 at 19:01
  • Hey @wooohoh please go through my answer! its working fine – Dupinder Singh Jul 11 '19 at 05:16
  • @Vincent , I updated a new answer and that trick can help, please have a look – Dupinder Singh Jul 25 '20 at 09:39

6 Answers6

6

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
Antal Alin
  • 156
  • 1
  • 8
0

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;
}
Eloise Maun
  • 78
  • 1
  • 5
  • My intention is to 'return t' and change return type to reflect it: somehow specifying the return type is an instance of cl. – Vincent Jul 06 '19 at 18:45
0

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).

Sergey
  • 7,184
  • 13
  • 42
  • 85
0

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);
Dupinder Singh
  • 7,175
  • 6
  • 37
  • 61
  • 1
    I want a single `checkTop` function. `t` can be changed at runtime. – Vincent Jul 11 '19 at 05:38
  • 1
    I just write `checkTop` function for referance (Just to show you), if you write single function and change `t` at run time, it will work fine, Just try this jscode template – Dupinder Singh Jul 11 '19 at 05:41
  • 1
    I tried but it doesn't work. `checkTop( Derived2 )` has a return type of Derived not Derived2 as I wanted. @typedef is doing nothing and there's no @return. – Vincent Jul 12 '19 at 02:46
0

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()
}
Ray Perea
  • 5,640
  • 1
  • 35
  • 38
-1

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

Dupinder Singh
  • 7,175
  • 6
  • 37
  • 61