Rory Hart
Rory Hart

Reputation: 1893

What is the correct usage of extends Function with Parameters and ReturnType

I'm trying to create my own memoization function for reselect. I had to redefine Parameters and ReturnType to accept extends Function (bug?). The signature for myMemoize is correct however the inner function signature isn't compiling.

import { createSelectorCreator } from "reselect";

type Parameters<T extends Function> = T extends (...args: infer P) => any ? P : never;
type ReturnType<T extends Function> = T extends (...args: any[]) => infer R ? R : any;

function myMemoize<F extends Function>(func: F): F {
  // ...
  return function(...args: Parameters<F>): ReturnType<F> {
    //...
    return func.apply(null, args)
  }
}

export const createMyMemoizeSelector = createSelectorCreator(myMemoize)

The error:

error TS2322: Type '(...args: Parameters<F>) => ReturnType<F>' is not assignable to type 'F'

Upvotes: 0

Views: 274

Answers (3)

Moa
Moa

Reputation: 1566

Although this answer doesn't reflect OP's specific implementation as closely as others, if you're looking for a simple typed memoize function, this works:

export function memoized<A, B>(f: (a: A) => B): (a: A) => B {
  const cache = new Map()
  return a => {
    if (!cache.has(a)) {
      const b = f(a)
      cache.set(a, b)
      return b
    }
    return cache.get(a)
  }
}

Upvotes: 0

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249536

The problem is that your new function is not really of the same type as the original. The original might have extra properties, that you do not forward to the return function.

You can just skip the return type, and also Parameters and ReturnType (both of which are defined so don't redefine them), are not really necessary for a full typed version :

function myMemoize<P extends any[], R>(func: (...a:P)=> R): (...a:P)=> R {
  // ...
  return function(...args: P): R {
    //...
    return func.apply(null, args)
  }
}

Not sure how this interacts with createSelectorCreator though.

Upvotes: 2

basarat
basarat

Reputation: 275809

Type '(...args: Parameters<F>) => ReturnType<F>' is not assignable to type 'F'

Fundamentally ...args implies any number of arguments, whereas F might take a very specific count.

Fix

You don't need Prameters / ReturnType for your use case as (func: F): F covers external type safety and you don't need internal type safety. Here is the simpler way:

function myMemoize<F extends Function>(func: F): F {
  // ...
  return function(...args: any[]): any {
    //...
    return func.apply(null, args)
  }
}

Upvotes: 0

Related Questions