Reputation: 10902
I am trying to annotate a property of an object which is an argument to a function.
Specifically I want options.length
explanation to appear in vscode when hovering over the function definition.
/**
* Memoize a function
* @param {(...fnArgs: T[]) => U} fn The function to memoize
* @param {Object} options Memoization options
* @param {number} options.max The max size of the LRU cache
* @param {number | undefined} options.length The response will be cached
* by the first N args, based on this value. Trailing args will still be
* passed to the underlying function but will be ignored during memoization.
*/
export const memo = <T, U>(
fn: (...fnArgs: T[]) => U,
{ max, length }: { max: number; length?: number }
) => {
const cachedArgs: T[][] = []
const cachedValues: U[] = []
const get = (args: T[]): U | undefined => {
const index = cachedArgs.findIndex(x => argsAreEqual(x, args, length))
if (index === -1) return
onUsed(index)
return cachedValues[index]
}
const set = (args: T[], value: U) => {
cachedArgs.push(args)
cachedValues.push(value)
}
const onUsed = (index: number) => {
moveToEnd(index, cachedArgs)
moveToEnd(index, cachedValues)
}
const prune = () => {
if (cachedArgs.length >= max) {
cachedArgs.shift()
cachedValues.shift()
}
}
return (...args: T[]) => {
let value = get(args)
if (value) return value
prune()
value = fn(...args)
set(args, value)
return value
}
}
When hovering over the type signature I get the following
@param fn — The function to memoize
@param options — Memoization options
I've done my best to copy from the docs but it doesn't show the explanation for options.length
.
I want it to show the explanation for how the options.length
parameter works. How can I do this?
Bonus question, not sure how to make the generics work with jsdoc either... help with @template
much appreciated!
Upvotes: 3
Views: 1287
Reputation: 327744
This behavior seems to be a known bug, whereby destructured parameters do not have their JSDoc comments displayed: microsoft/TypeScript#24746. I'm not really sure why, but according to a comment, giving a @param
a type of Object
is equivalent to giving it a type of any
, and you can get the behavior you want by using a different type. In the following I change Object
to {}
:
/**
* @param {(...fnArgs: T[]) => U} fn
* @param {{}} options Memoization options
* @param {number} options.max The max size of the LRU cache
* @param {number | undefined} options.length The response will be cached
* by the first N args, based on this value. Trailing args will still be
* passed to the underlying function but will be ignored during memoization.
*/
export const memo = <T, U>(
fn: (...fnArgs: T[]) => U,
{ max, length }: { max: number; length?: number }
) => { }
and your IntelliSense starts to work, at least when hovering over the function. (I don't think it's possible to get comments when you hover over the actual identifier named max
or length
in your code). Observe:
/* IntelliSense shows:
const memo: <T, U>(fn: (...fnArgs: T[]) => U, { max, length }: {
max: number;
length?: number | undefined;
}) => void
@param fn
@param options — Memoization options
@param options.max — The max size of the LRU cache
@param options.length — The response will be cached by the first N args,
based on this value. Trailing args will still be passed to the
underlying function but will be ignored during memoization.
*/
PLEASE NOTE that this only seems to work for TypeScript code (like .ts
or .tsx
) and not JavaScript code. If you do this in JavaScript, the compiler will complain about anything other than Object
or object
.
As for your bonus question, I think it should work in TypeScript just like it does in JavaScript:
/**
* @template T the arg type of fn
* @template U the return type of fn
* @param {(...fnArgs: T[]) => U} fn
which shows me
/* IntelliSense shows:
const memo: <T, U>(fn: (...fnArgs: T[]) => U, { max, length }: {
max: number;
length?: number | undefined;
}) => void
@template — T the arg type of fn
@template — U the return type of fn
@param fn
*/
Upvotes: 3