typescript - JSDoc: @callback med @property?


Jag använder JSDoc och har följande definition för Stream<T>:

/**
 * @template T
 * @callback Stream
 *
 * @param {T} [value]
 *
 * @return {T} the value
 */

Detta översätts korrekt till följande definition i index.d.ts:

export type Stream<T> = (value?: T) => T;

Men en Stream är en funktion som också har en map-egenskap. map är en funktion som skapar en annan stream:

const s = stream(); // s is a Stream
s(5); // s is a function
// but s.map is also a function
const s2 = s.map(x => x + 1);

Hur definierar jag Stream<T> med JSDoc? Följande fungerar inte:

/**
 * @template T
 * @callback Stream
 *
 * @param {T} [value]
 * @property {Function} map
 *
 * @return {T} the value
 */

Det genererar inte map och ännu värre, förlorar faktumet att funktionen returnerar T:

export type Stream<T> = (value?: T) => any;

Idealiskt skulle jag vilja att detta (eller motsvarande) genereras:

export interface Stream<T> {
  (value?: T) => T;
  map<U>(fn: (value: T) => U): Stream<U>;
}

Finns det ett sätt att uppnå detta med JSDoc?

Svar #1

När det gäller VSCode:s intellisense har jag kommit fram till att implementera något liknande, för att definiera typ för callback med hjälp av JSDoc. Det kan också fungera i det här fallet.

Jag har inte stött på att definiera gränssnittstyper direkt i JSDoc-kommentarer, men det kan definieras direkt på den faktiska implementeringen. Först definieras funktionens ström:

/**
 * @template T
 * @function Stream
 *
 * @param {T} [value]
 *
 * @return {T} the value
 */
function Stream(value) {
  //@ts-ignore
  return value;
}

Jag ändrade @callback-taggen till @function, eftersom annars typen Stream blir fel tillsammans med Stream.map-egenskapen som definierats:

Stream(); // (local function) Stream(value: any): any

Nästa definieras egenskapskartan.

/**
 * @template T inputted value
 * @template U mapped value
 *
 * @param {(value: T) => U} fn
 */
Stream.map = function (fn) {
  return fn;
};

Som ett resultat är s en 'interface'-funktion med map-egenskap som en metod.

// intellisense shows type similar to asked Stream interface:
// const s: {
//   <T>(value?: T | undefined): T;
//   map<T, U>(fn: (value: T) => U): (value: T) => U;
// }
const s = Stream;
// const s: <5>(value?: 5 | undefined) => 5
s(5);
const s2 = s.map(
  /** @param {number} x */
  x => x + 1
);
// const s2: (value: number) => number
s2(8);
// const s3: (value: string) => Error
const s3 = s.map(
  /** @param {string} x */
  x => new Error(x)
);