Skip to content

Commit 11a1b21

Browse files
authored
Merge pull request #224 from webdeveric/dev
Added `memo()`
2 parents 3af0a2c + b257f0a commit 11a1b21

4 files changed

Lines changed: 200 additions & 44 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"url": "https://github.com/webdeveric/utils/issues"
6767
},
6868
"homepage": "https://github.com/webdeveric/utils/#readme",
69-
"packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017",
69+
"packageManager": "pnpm@10.31.0+sha512.e3927388bfaa8078ceb79b748ffc1e8274e84d75163e67bc22e06c0d3aed43dd153151cbf11d7f8301ff4acb98c68bdc5cadf6989532801ffafe3b3e4a63c268",
7070
"scripts": {
7171
"clean": "rimraf ./dist/",
7272
"prebuild": "pnpm clean",
@@ -87,7 +87,7 @@
8787
"devDependencies": {
8888
"@commitlint/config-conventional": "^20.4.3",
8989
"@commitlint/types": "^20.4.3",
90-
"@types/node": "^24.11.0",
90+
"@types/node": "^24.12.0",
9191
"@vitest/coverage-v8": "^4.0.18",
9292
"@webdeveric/eslint-config-ts": "^0.12.0",
9393
"@webdeveric/prettier-config": "^0.3.0",

pnpm-lock.yaml

Lines changed: 46 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/memo.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { AnyFunction } from './types/common.js';
2+
3+
export type CacheKeyFn<Fn extends AnyFunction, ReturnValue = unknown> = (args: Parameters<Fn>) => ReturnValue;
4+
5+
export type DefaultCacheKeyFn<Fn extends AnyFunction> = CacheKeyFn<Fn, NonNullable<Parameters<Fn>[0]> | string>;
6+
7+
export type MemoizedFn<Fn extends AnyFunction, CkFn extends CacheKeyFn<Fn>> = Fn & {
8+
cache: Map<ReturnType<CkFn>, ReturnType<Fn>>;
9+
};
10+
11+
/**
12+
* Memoize a function
13+
*
14+
* @todo use `Map.getOrInsertComputed()` once it is widely available.
15+
*/
16+
export function memo<Fn extends AnyFunction, CkFn extends CacheKeyFn<Fn>>(
17+
fn: Fn,
18+
getCacheKey: CkFn,
19+
): MemoizedFn<Fn, CkFn>;
20+
21+
export function memo<Fn extends AnyFunction>(fn: Fn): MemoizedFn<Fn, DefaultCacheKeyFn<Fn>>;
22+
23+
export function memo<Fn extends AnyFunction, CkFn extends CacheKeyFn<Fn>>(
24+
fn: Fn,
25+
getCacheKey?: CkFn,
26+
): MemoizedFn<Fn, CkFn> {
27+
const defaultGetCacheKey: DefaultCacheKeyFn<Fn> = (args) => args[0] ?? JSON.stringify(args);
28+
29+
const resolvedGetCacheKey = getCacheKey ?? defaultGetCacheKey;
30+
31+
const memoized = (...args: Parameters<Fn>): ReturnType<Fn> => {
32+
const key = resolvedGetCacheKey(args);
33+
34+
if (memoized.cache.has(key)) {
35+
return memoized.cache.get(key);
36+
}
37+
38+
const value = fn(...args);
39+
40+
memoized.cache.set(key, value);
41+
42+
return value;
43+
};
44+
45+
memoized.cache = new Map();
46+
47+
return memoized as MemoizedFn<Fn, CkFn>;
48+
}

0 commit comments

Comments
 (0)