Skip to content

withCallCount

Edit

Wraps a function and tracks how many times the wrapper is invoked.

withCallCount.ts
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;
};
Terminal
wget -O src/lib/withCallCount.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/functions/withCallCount.ts
withCallCount.example.ts
import { withCallCount } from "./withCallCount";
const add = withCallCount((left: number, right: number) => left + right);
console.log(add(3, 5)); // 8
console.log(add(2, 7)); // 9
console.log(add.getCallCount()); // 2
add.resetCallCount();
console.log(add.getCallCount()); // 0
  • fn ((...args: A) => R) — Function whose wrapper invocations should be counted; its receiver is preserved.

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.