3

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

enter image description here

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!

4
  • I've never used this feature, but maybe you could use @prop or @property instead, like this? Commented Mar 11, 2021 at 14:43
  • Ah, this is a known bug apparently: microsoft/TypeScript#24746, with possible workarounds listed in there. Or maybe it's just not supported with TS code, as in microsoft/TypeScript#11859 Commented Mar 11, 2021 at 14:45
  • 1
    Does this workaround work for you? I have no earthly idea why Object would prevent @param from working, but the linked issues seemed to indicate that would happen. Commented Mar 11, 2021 at 15:11
  • Brilliant! If you write in an answer will approve asap. Otherwise happy to take the credit myself and write the answer! hehe 🙈 @jcalz Commented Mar 11, 2021 at 15:21

1 Answer 1

3

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
*/

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.