Steven Guerrero
Steven Guerrero

Reputation: 1056

How to pass a generic type argument with JSDoc?

Before giving up I wanted to give it a shot here.

I have a definition file with typings like the following:

/**
 * My json decode function, in reality very different
 * implementation (class like) but it works as an example
 */
  function decodeJSON<T = unknown>(str: string): T;

If I wanted to use this generic in TypeScript I would do something like the following

const value = decodeJSON<number[]>("[1,2,3]"); // return type will be number[]

However in my actual code I can't use TypeScript, only it's typings for development purposes, and I can't seem to find a way to tell my editor what the type I'm passing to the generic parameter is so that I get the correct type information.

I've tried to use JSDoc to specify what the parameter for the generic might be the same way that TypeScript can

// I was expecting JSDoc to understand something like this, but it doesn't
/** @type {decodeJSON<number[]>} */
const value = decodeJSON("[1,2,3]"); // Type inference will be unknown

But it doesn't work. I don't really care what the result might be in runtime, I have sanity checks for that already implemented. I just want my editor to give me type hints about the generic function result to make my job (this function has some heavy use in my app) easier

My editor is WebStorm in case it matters, but this seems to be a general limitation of JSDoc

Upvotes: 21

Views: 10468

Answers (9)

Fernando Piancastelli
Fernando Piancastelli

Reputation: 990

As of Dec/2024 this is the recommended approach by JSDocs on how to document the use of generics with TypeScript

/**
 * Decode a JSON using T
 *
 * @template T
 * @param {string} str
 * @returns {T}
 */
 function decodeJson<T = unknown>(str: string): T {
   return str as T;
 }

WebStorm 2024 was warning about "T" (generic) and that's how I got here and to this Answer Article

Upvotes: 0

崮生1
崮生1

Reputation: 56

My idea is to just define the full function type, e.g.:

/**
 * @type {<F extends (...arg: any[]) => any>(p: F)=> (...p: Parameters<F>) => NonNullable<ReturnType<F>>}
 */

Upvotes: 0

Tordek
Tordek

Reputation: 10882

The answer I found the most satisfying was:

const value = /** @type {decodeJSON<number[]>} */ (decodeJSON)("[1,2,3]"); // Type inference will be unknown

Note the extra parentheses atound decodeJSON.

Upvotes: 2

moomoolive
moomoolive

Reputation: 172

I found a working solution to the question, but it's quite ugly. You were actually quite close with your original solution.

Essentially you cast the generic function to a more narrow version of itself via type cast. Here's the code:

/**
 * @template {unknown} T
 * @param {string} jsonStr
 * @return {T}
 */
function decodeJson(jsonStr) {
    return JSON.parse(jsonStr)
}

// narrow the generic via a type cast
let arr = /** @type {typeof decodeJson<number[]>} */ (decodeJson)("[1, 2, 3]")
console.log(arr) // arr should be typeof "number[]" now

Here's a link to the Typescript playground.

Although the above solution works it may actually be a better idea to just cast the value after returning from the function, like so:

let arr = /** @type {number[]} */ (decodeJson("[1, 2, 3]"))

It's much more concise.

Upvotes: 1

Junior Usca
Junior Usca

Reputation: 513

Taking into account the problem

const value = decodeJSON<number[]>("[1,2,3]"); // return type will be number[]

I found something more practical in Github for jsDoc

/** @type {ReturnType<typeof decodeJSON<number[]>>} */
const value = decodeJSON("[1,2,3]");

Upvotes: 9

Sande265
Sande265

Reputation: 1

/** 
* @type {string} str
* @returns {number[]} // This infers the type to be returned.
*/
function decodeJSON(str) {
   // ... your logic
};

Upvotes: 0

Manu Artero
Manu Artero

Reputation: 10273

My 2 cents if someone came here searching how to type a ts Generic <T> using JsDoc;

this ts

function identity<T>(arg: T): T {
  return arg;
}

could be achieved as:

/**
 * @template T
 * @param {T} arg
 * @return {T}
 */
function identity(arg) {
  return arg;
}

then:

identity({value: 42}).
                     ^ suggests value: number

Upvotes: 9

balogunofafrica
balogunofafrica

Reputation: 83

You can pass generic types using Jsdoc like so:

const value = decodeJSON(/** @type {number[]} */("[1,2,3]"))

Upvotes: 4

Steven Guerrero
Steven Guerrero

Reputation: 1056

Apparently this isn't something that JSDoc can do, even though the need for it is kinda real

https://github.com/microsoft/TypeScript/issues/27387

Upvotes: 18

Related Questions