Skip to content

Commit

Permalink
adding MemoizeFn generic
Browse files Browse the repository at this point in the history
  • Loading branch information
alexreardon committed Oct 18, 2021
1 parent 8b12bfb commit ae7a643
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 3 deletions.
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ An equality function should return `true` if the arguments are equal. If `true`

Equality functions are not called if the `this` context of the function has changed (see below).

Here is an example that uses a [dequal](https://github.com/lukeed/dequal) deep equal equality check
Here is an example that uses a [lodash.isEqual](https://lodash.com/docs/4.17.15#isEqual) deep equal equality check

> `lodash.isequal` correctly handles deep comparing two arrays
Expand Down Expand Up @@ -468,6 +468,62 @@ There is no (great) way to correctly set the `.length` property of the memoized

[→ discussion](https://github.com/alexreardon/memoize-one/pull/124).

## Memoized function `type`

The resulting function you get back from `memoize-one` has *almost* the same `type` as the function that you are memoizing

```ts
declare type MemoizedFn<TFunc extends (this: any, ...args: any[]) => any> = {
clear: () => void;
(this: ThisParameterType<TFunc>, ...args: Parameters<TFunc>): ReturnType<TFunc>;
};
```

- the same call signature as the function being memoized
- a `.clear()` function property added
- other function object properties on `TFunc` as not carried over

You are welcome to use the `MemoizedFn` generic directly from `memoize-one` if you like:

```ts
import memoize, { MemoizedFn } from 'memoize-one';
import isDeepEqual from 'lodash.isequal';

// Takes any function: TFunc, and returns a Memoized<TFunc>
function withDeepEqual<TFunc extends (...args: any[]) => any>(fn: TFunc): MemoizedFn<TFunc> {
return memoize(fn, isDeepEqual);
}

function add(first: number, second: number): number {
return first + second;
}

const memoized = withDeepEqual(add);

expectTypeOf<typeof memoized>().toEqualTypeOf<MemoizedFn<typeof add>>();
```

In this specific example, this type would have been correctly inferred too

```ts
import memoize, { MemoizedFn } from 'memoize-one';
import isDeepEqual from 'lodash.isequal';

// return type of MemoizedFn<TFunc> is inferred
function withDeepEqual<TFunc extends (...args: any[]) => any>(fn: TFunc) {
return memoize(fn, isDeepEqual);
}

function add(first: number, second: number): number {
return first + second;
}

const memoized = withDeepEqual(add);

// type test still passes
expectTypeOf<typeof memoized>().toEqualTypeOf<MemoizedFn<typeof add>>();
```

## Performance 🚀

### Tiny
Expand Down
1 change: 1 addition & 0 deletions src/memoize-one.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type MemoizedFn<TFunc extends (this: any, ...args: any[]) => any> = {
(this: ThisParameterType<TFunc>, ...args: Parameters<TFunc>): ReturnType<TFunc>;
};

// internal type
type Cache<TFunc extends (this: any, ...args: any[]) => any> = {
lastThis: ThisParameterType<TFunc>;
lastArgs: Parameters<TFunc>;
Expand Down
40 changes: 39 additions & 1 deletion test/types-test.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { EqualityFn } from './../src/memoize-one';
import isDeepEqual from 'lodash.isequal';
import type { EqualityFn, MemoizedFn } from './../src/memoize-one';
import { expectTypeOf } from 'expect-type';
import memoize from '../src/memoize-one';

Expand Down Expand Up @@ -30,6 +31,43 @@ it('should add a .clear function property', () => {
expectTypeOf<typeof memoized.clear>().toEqualTypeOf<() => void>();
});

it('should return a `MemoizedFn<T>`', () => {
function add(first: number, second: number): number {
return first + second;
}

const memoized = memoize(add);

expectTypeOf<typeof memoized>().toEqualTypeOf<MemoizedFn<typeof add>>();
});

it('should allow you to leverage the MemoizedFn generic to allow many memoized functions', () => {
function withDeepEqual<TFunc extends (...args: any[]) => any>(fn: TFunc): MemoizedFn<TFunc> {
return memoize(fn, isDeepEqual);
}
function add(first: number, second: number): number {
return first + second;
}

const memoized = withDeepEqual(add);

expectTypeOf<typeof memoized>().toEqualTypeOf<MemoizedFn<typeof add>>();
});

it('should return a memoized function that satisies a typeof check for the original function', () => {
function add(first: number, second: number): number {
return first + second;
}
function caller(fn: typeof add) {
return fn(1, 2);
}
const memoized = memoize(add);

// this line is the actual type test
const result = caller(memoized);
expectTypeOf<typeof result>().toEqualTypeOf<number>();
});

it('should type the equality function to based on the provided function', () => {
function add(first: number, second: number) {
return first + second;
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"removeComments": true,
"esModuleInterop": true,
"esModuleInterop": true
},
"include": ["src/", "test/"]
}

0 comments on commit ae7a643

Please sign in to comment.