Skip to content

Commit 460dd9f

Browse files
🤖 Merge PR DefinitelyTyped#74510 Enable hooker to infer the parameter and return value types of function by @TransparentLC
1 parent 1383289 commit 460dd9f

File tree

2 files changed

+181
-71
lines changed

2 files changed

+181
-71
lines changed

types/hooker/hooker-tests.ts

Lines changed: 76 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,96 @@
11
import hooker = require("hooker");
22

33
function tests() {
4-
var objectToHook: any = {
5-
hello: "world",
6-
};
7-
hooker.hook(objectToHook, "hello", () => {});
8-
hooker.hook(objectToHook, "hello", (): any => {
9-
return null;
10-
});
11-
hooker.hook(objectToHook, ["hello", "foo"], () => {});
12-
hooker.hook(objectToHook, ["hello", "bar"], (): any => {
13-
return null;
14-
});
15-
hooker.hook(objectToHook, "bar", () => {
16-
return hooker.filter(this, ["foo", "bar"]);
17-
});
18-
hooker.hook(objectToHook, "bar", () => {
19-
return hooker.override("good");
4+
hooker.hook(Math, "max", function(
5+
...args // $ExpectType number[]
6+
) {});
7+
hooker.unhook(Math, "max");
8+
9+
hooker.hook(Math, "max", function() {
10+
if (arguments.length === 0) {
11+
return hooker.override(9000);
12+
}
2013
});
21-
hooker.hook(objectToHook, "bar", () => {
22-
return hooker.preempt("good");
14+
// @ts-expect-error
15+
hooker.hook(Math, "max", function() {
16+
if (arguments.length === 0) {
17+
return hooker.override("not a number");
18+
}
2319
});
24-
hooker.orig(objectToHook, "hello");
25-
hooker.orig(objectToHook, ["hello", "foo"]);
26-
hooker.hook(objectToHook, "foo", {
27-
pre: () => {},
20+
21+
hooker.hook(Math, "max", {
22+
once: true,
23+
pre: function() {},
2824
});
29-
hooker.hook(objectToHook, "foo", {
30-
pre: () => {
31-
return hooker.preempt(1);
25+
26+
hooker.hook(Math, "max", {
27+
pre: function(...args) {
28+
return hooker.filter(this, args.map(num => num * 2));
3229
},
3330
});
34-
hooker.hook(objectToHook, "foo", {
35-
pre: () => {
36-
return hooker.override(1);
31+
32+
// @ts-expect-error
33+
hooker.hook(Math, "max", {
34+
pre: function(...args) {
35+
return hooker.filter(this, args.map(num => num.toString()));
3736
},
3837
});
39-
hooker.hook(objectToHook, "foo", {
40-
pre: () => {
41-
return hooker.filter(1, ["abc"]);
38+
39+
hooker.hook(Math, "max", {
40+
post: function(result) {
41+
return hooker.override(result * 1000);
4242
},
4343
});
44-
hooker.hook(objectToHook, "foo", {
45-
post: () => {},
44+
45+
hooker.orig(Math, "max"); // $ExpectType (...values: number[]) => number
46+
47+
hooker.hook(Math, Object.getOwnPropertyNames(Math), {
48+
passName: true,
49+
pre: function(name) {},
50+
post: function(result, name) {},
4651
});
47-
hooker.hook(objectToHook, "foo", {
48-
post: () => {
49-
return hooker.filter(1, ["abc"]);
52+
53+
hooker.hook(Number, "parseInt", function(
54+
string, // $ExpectType string
55+
radix, // $ExpectType number | undefined
56+
) {});
57+
58+
hooker.hook(Number, "parseInt", {
59+
pre(
60+
string, // $ExpectType string
61+
radix, // $ExpectType number | undefined
62+
) {},
63+
post(
64+
result, // $ExpectType number
65+
string, // $ExpectType string
66+
radix, // $ExpectType number | undefined
67+
) {},
68+
});
69+
70+
hooker.hook(Number, "parseInt", {
71+
pre(string, radix) {
72+
return hooker.preempt(0);
5073
},
5174
});
52-
hooker.hook(objectToHook, "foo", {
53-
once: false,
75+
76+
// @ts-expect-error
77+
hooker.hook(Number, "parseInt", {
78+
pre(string, radix) {
79+
return hooker.preempt("not a number");
80+
},
5481
});
55-
hooker.hook(objectToHook, "foo", {
56-
passName: true,
82+
83+
hooker.hook(Number, "parseInt", {
84+
pre(string, radix) {
85+
return hooker.filter(this, [string, radix] as [string, number | undefined]);
86+
},
5787
});
58-
hooker.hook(objectToHook, "foo", {
59-
pre: () => {},
60-
post: () => {
61-
return hooker.filter(this, []);
88+
// @ts-expect-error
89+
hooker.hook(Number, "parseInt", {
90+
pre(string, radix) {
91+
return hooker.filter(this, [0, "not a number"]);
6292
},
63-
once: true,
64-
passName: false,
6593
});
94+
95+
hooker.orig(Number, "parseInt"); // $ExpectType (string: string, radix?: number | undefined) => number
6696
}

types/hooker/index.d.ts

Lines changed: 105 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,118 @@
1-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
2-
declare type HookerPostHookFunction = (result: any, ...args: any[]) => IHookerPostHookResult | void;
3-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
4-
declare type HookerPreHookFunction = (...args: any[]) => IHookerPreHookResult | void;
1+
/* eslint-disable @typescript-eslint/no-invalid-void-type */
2+
declare type HookerPreHookFunction<F extends (...args: any[]) => any = (...args: any[]) => any> = (
3+
...args: Parameters<F>
4+
) => HookerOverride<ReturnType<F>> | HookerPreempt<ReturnType<F>> | HookerFilter<Parameters<F>> | Promise<void> | void;
5+
declare type HookerPostHookFunction<F extends (...args: any[]) => any = (...args: any[]) => any> = (
6+
result: ReturnType<F>,
7+
...args: Parameters<F>
8+
) => HookerOverride<ReturnType<F>> | Promise<void> | void;
9+
/* eslint-enable @typescript-eslint/no-invalid-void-type */
510

611
declare module "hooker" {
7-
function hook(object: any, props: string | string[], options: IHookerOptions): void;
8-
function hook(object: any, props: string | string[], prehookFunction: HookerPreHookFunction): void;
12+
/**
13+
* Monkey-patch (hook) one or more methods of an object. Returns an array of hooked method names.
14+
*
15+
* @param object
16+
* @param props Can be a method name, array of method names or null. If null (or omitted), all enumerable methods of `object` will be hooked.
17+
* @param options
18+
*/
19+
function hook<T, K extends keyof T>(
20+
object: T,
21+
props: K,
22+
options: IHookerOptions<T[K] extends (...args: any[]) => any ? T[K] : never>,
23+
): string[];
24+
/**
25+
* Monkey-patch (hook) one or more methods of an object. Returns an array of hooked method names.
26+
*
27+
* @param object
28+
* @param props Can be a method name, array of method names or null. If null (or omitted), all enumerable methods of `object` will be hooked.
29+
* @param prehookFunction A pre-hook function to be executed before the original function. Arguments passed into the method will be passed into the pre-hook function as well.
30+
*/
31+
function hook<T, K extends keyof T>(
32+
object: T,
33+
props: K,
34+
prehookFunction: HookerPreHookFunction<T[K] extends (...args: any[]) => any ? T[K] : never>,
35+
): string[];
36+
/**
37+
* Monkey-patch (hook) one or more methods of an object. Returns an array of hooked method names.
38+
*
39+
* @param object
40+
* @param props Can be a method name, array of method names or null. If null (or omitted), all enumerable methods of `object` will be hooked.
41+
* @param options
42+
*/
43+
function hook(object: any, props: string[], options: IHookerOptions): string[];
44+
/**
45+
* Monkey-patch (hook) one or more methods of an object. Returns an array of hooked method names.
46+
*
47+
* @param object
48+
* @param props Can be a method name, array of method names or null. If null (or omitted), all enumerable methods of `object` will be hooked.
49+
* @param prehookFunction A pre-hook function to be executed before the original function. Arguments passed into the method will be passed into the pre-hook function as well.
50+
*/
51+
function hook(object: any, props: string[], prehookFunction: HookerPreHookFunction): string[];
52+
/**
53+
* Un-monkey-patch (unhook) one or more methods of an object.
54+
*
55+
* @param object
56+
* @param props Can be a method name, array of method names or null. If null (or omitted), all methods of object will be unhooked.
57+
*/
958
function unhook(object: any, props?: string | string[]): string[];
10-
function orig(object: any, props: string | string[]): Function;
11-
function override(value: any): HookerOverride;
12-
function preempt(value: any): HookerPreempt;
13-
function filter(context: any, args: any[]): HookerFilter;
59+
/**
60+
* Get a reference to the original method from a hooked function.
61+
*
62+
* @param object
63+
* @param prop
64+
*/
65+
function orig<T, K extends keyof T>(object: T, prop: K): T[K] extends (...args: any[]) => any ? T[K] : never;
66+
function orig(object: any, prop: string): Function;
67+
/**
68+
* When a pre- or post-hook returns the result of this function, the value passed will be used in place of the original function's return value. Any post-hook override value will take precedence over a pre-hook override value.
69+
*
70+
* @param value
71+
*/
72+
function override<T>(value: T): HookerOverride<T>;
73+
/**
74+
* When a pre-hook returns the result of this function, the value passed will be used in place of the original function's return value, and the original function will **NOT** be executed.
75+
*
76+
* @param value
77+
*/
78+
function preempt<T>(value: T): HookerPreempt<T>;
79+
/**
80+
* When a pre-hook returns the result of this function, the context and arguments passed will be applied into the original function.
81+
*
82+
* @param context
83+
* @param args
84+
*/
85+
function filter<T>(context: any, args: T): HookerFilter<T>;
1486
}
1587

16-
declare class HookerOverride implements IHookerPostHookResult, IHookerPreHookResult {
17-
value: any;
88+
declare class HookerOverride<T> {
89+
value: T;
1890
}
1991

20-
declare class HookerPreempt implements IHookerPreHookResult {
21-
value: any;
92+
declare class HookerPreempt<T> {
93+
value: T;
2294
}
2395

24-
declare class HookerFilter implements IHookerPreHookResult {
96+
declare class HookerFilter<T> {
2597
context: any;
26-
args: any[];
98+
args: T;
2799
}
28100

29-
interface IHookerPostHookResult {}
30-
31-
interface IHookerPreHookResult {}
32-
33-
interface IHookerOptions {
34-
pre?: HookerPreHookFunction | undefined;
35-
post?: HookerPostHookFunction | undefined;
36-
once?: boolean | undefined;
37-
passName?: boolean | undefined;
101+
interface IHookerOptions<F extends (...args: any[]) => any = (...args: any[]) => any> {
102+
/**
103+
* A pre-hook function to be executed before the original function. Arguments passed into the method will be passed into the pre-hook function as well.
104+
*/
105+
pre?: HookerPreHookFunction<F>;
106+
/**
107+
* A post-hook function to be executed after the original function. The original function's result is passed into the post-hook function as its first argument, followed by the method arguments.
108+
*/
109+
post?: HookerPostHookFunction<F>;
110+
/**
111+
* If true, auto-unhook the function after the first execution.
112+
*/
113+
once?: boolean;
114+
/**
115+
* If true, pass the name of the method into the pre-hook function as its first arg (preceding all other arguments), and into the post-hook function as the second arg (after result but preceding all other arguments).
116+
*/
117+
passName?: boolean;
38118
}

0 commit comments

Comments
 (0)