withCallCount
EditWraps a function and tracks how many times the wrapper is invoked.
export type CallCountControls = { readonly getCallCount: () => number; readonly resetCallCount: () => void;};
export type CountedFunction< Arguments extends unknown[], Return, This = unknown,> = (( this: This, ...args: Arguments) => Return) & CallCountControls;
type AnyFunction = (this: never, ...args: never[]) => unknown;type CallCountGuard<Function extends AnyFunction> = Extract<keyof Function, keyof CallCountControls> extends never ? [] : never;
/** Wraps a function and tracks how many times the wrapper is invoked. */export const withCallCount = <Function extends AnyFunction>( fn: Function, ..._guard: CallCountGuard<Function>): Function & CallCountControls => { if ("getCallCount" in fn || "resetCallCount" in fn) { throw new TypeError( 'fn must not define "getCallCount" or "resetCallCount" properties' ); }
let callCount = 0;
const getCallCount = () => callCount; const resetCallCount = () => { callCount = 0; };
return new Proxy(fn, { apply(target, receiver, args) { callCount += 1; return Reflect.apply(target, receiver, args); }, get(target, property, receiver) { if (property === "getCallCount") return getCallCount; if (property === "resetCallCount") return resetCallCount; return Reflect.get(target, property, receiver); }, has(target, property) { return ( property === "getCallCount" || property === "resetCallCount" || Reflect.has(target, property) ); }, set(target, property, value, receiver) { return property === "getCallCount" || property === "resetCallCount" ? false : Reflect.set(target, property, value, receiver); }, }) as Function & CallCountControls;};Download
Section titled “Download”wget -O src/lib/withCallCount.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/functions/withCallCount.tstemp_dir="$(mktemp -d)" && bunx degit jrTilak/lazykit/registry/functions "$temp_dir" && mv "$temp_dir/withCallCount.ts" src/lib/withCallCount.ts && rm -r "$temp_dir"Examples
Section titled “Examples”import { withCallCount } from "./withCallCount";
const add = withCallCount((left: number, right: number) => left + right);
console.log(add(3, 5)); // 8console.log(add(2, 7)); // 9console.log(add.getCallCount()); // 2
add.resetCallCount();console.log(add.getCallCount()); // 0fn((...args: A) => R) — Function whose wrapper invocations should be counted; its receiver is preserved.
Returns
Section titled “Returns”The wrapped function with getCallCount() and resetCallCount() methods. Calls are counted even when fn throws.
Properties on a callable object remain available through the wrapper. Callables
that already define either control name are rejected.