Reputation: 22923
Having static member of a function is quite a common thing in javascript. Say this example:
function createSpy(fn){
return function spy() {
if(!spy.callCount) spy.callCount = 0;
spy.callCount++;
fn.apply(arguments);
}
}
Here the functions created by createSpy
have a static variable callCount
. I have trouble finding out how to type these kinds of objects correctly in TypeScript, as the only examples I have seen of such a thing is the explicit support for static
members in a TypeScript class.
Say I have an existing js library and would like to add proper typing for the resulting types in a library.d.ts
file, what would I need in order to get the following to pass?
const mySpy: Spy = createSpy(myUtil);
mySpy();
console.log(mySpy.callCount === 1);
Upvotes: 0
Views: 64
Reputation: 31803
The best you can do in a TypeScript implementation is
function createSpy<A extends any[]>(fn: (...args: A) => void) {
function spy(...args: A): void {
spy.callCount++;
fn(...args);
};
spy.callCount = 0;
return spy;
}
The vastly improved, although still somewhat lacking assignment syntax leveraged above was introduced in TypeScript 3.1 - Properties declarations on functions.
If you are writing a declaration file for an existing JavaScript implementation you have a few options.
A decent one aligned with your example usage is
type Spy<F extends (...args: any[]) => void> = F & { callCount: number };
declare function createSpy<F extends (...args: any[]) => void>(fn: F): Spy<F>;
Which would be consumed as
function myUtil() { console.log('something useful'); }
const mySpy: Spy<typeof myUtil> = createSpy(myUtil);
mySpy();
console.log(mySpy.callCount === 1);
but consumers should be encouraged to take advantage of type inference
const mySpy = createSpy(myUtil); // Spy<() => void>
mySpy();
console.log(mySpy.callCount === 1);
Alternately, and considering the benefits of inference, we could might opt to write it in a single declaration
declare function createSpy<F extends (...args: any[]) => void>(fn: F): F & {
callCount: number
};
Upvotes: 1