TrogloGeek
TrogloGeek

Reputation: 111

How to use @template in function JSDoc to create coherence between param and return type?

I use @ts-check and ts linter on an ecmascript projet (I cannot use typescript but want its formalism).

All is going well except for one thing: I want to use JSDoc generics declaration so that return type is tied-to/inferred-from a parameter type:

/**
 * @template V - Type de valeurs de la map.
 * @param {Map<string, V>} map
 * @returns {Object.<string, V>}
 */
function objectifyMap(map) {
    /** @type {Object.<string,V>} */
    const result = {};
    map.forEach((value, key) => { result[key] = value; });
    return result;
}

/**
 * @template V
 * @param {Object.<string, V>} object
 * @returns {Map<string, V>}
 */
function mappifyObject(object) {
    /** @type {Map<string,V>} */
    const result = new Map();
    Object.entries(object).forEach(([key, value]) => { result.set(key, value); });
    return result;
}

/** @type {Object.<string, string>} */
const obj = {};

/** @type {Map<string, string>} */
let map;
map = mappifyObject(obj);

But on the last line, ts linter throws an error on obj parameter:

Argument of type '{ [x: string]: string; }' is not assignable to parameter of type '{ [x: string]: V; }'.
  Index signatures are incompatible.
    Type 'string' is not assignable to type 'V'.
      'V' could be instantiated with an arbitrary type which could be unrelated to 'string'.ts(2345)

I guess ts linter is expecting map = mappifyObject<string>(obj); but as I'm only using @ts-check and not the typescript transpiler, I cannot do that.

Does anyone knows how to circumvent that?

Upvotes: 3

Views: 2600

Answers (1)

ocket8888
ocket8888

Reputation: 1140

I'm not familiar with the Object.<K, V> type syntax you're using. In actual TypeScript, the typing would be either {[x: string]: V} or Record<string, V>. Either one of those typings works for me:

/**
 * @template V
 * @param {{[x: string]: V}} object
 * @returns {Map<string, V>}
 */
function mappifyObject(object) {
    /** @type {Map<string,V>} */
    const result = new Map();
    Object.entries(object).forEach(([key, value]) => { result.set(key, value); });
    return result;
}

/** @type {{[x: string]: string}} */
const obj = {};

/** @type {Map<string, string>} */
let map;
map = mappifyObject(obj);

or

/**
 * @template V
 * @param {Record<string, V>} object
 * @returns {Map<string, V>}
 */
function mappifyObject(object) {
    /** @type {Map<string,V>} */
    const result = new Map();
    Object.entries(object).forEach(([key, value]) => { result.set(key, value); });
    return result;
}

/** @type {Record<string, string>} */
const obj = {};

/** @type {Map<string, string>} */
let map;
map = mappifyObject(obj);

Both work without errors.

Upvotes: 1

Related Questions