//@ts-check

const pickFirstArg = (fst, ..._none) => JSON.stringify(fst)

/**
 * Caches the result of a function.
 * The cache is available as `.cache` in case there's a need to clear anything.
 * Uses the first parameter as a key by default, you can change this behavior by passing a custom
 * hash function.
 * @template {(...args: any[]) => any} T
 * @param {T} functionToMemoize 
 * @param {(...args: Parameters<T>) => string|number} [hashFunction] 
 * @returns 
 */
export const memoize = (functionToMemoize, hashFunction = pickFirstArg) => {
  /** @type {Map<string|number, ReturnType<T>>} */
  const cache = new Map()
  /**
   * 
   * @param  {Parameters<T>} args 
   * @returns {ReturnType<T>}
   */
  const memoized = (...args) => {
    const key = hashFunction(...args)
    if(!cache.has(key)){
      cache.set(key, functionToMemoize(...args))
    }
    return cache.get(key)
  }

  return Object.assign(memoized, { cache })
}

export default memoize