Skip to content

Commit de5dbd2

Browse files
committed
Use conditional type instead of function overloading for component events
1 parent f2b4f23 commit de5dbd2

File tree

4 files changed

+39
-70
lines changed

4 files changed

+39
-70
lines changed

src/LiveComponent/assets/dist/Component/index.d.ts

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,21 @@ import BackendRequest from '../Backend/BackendRequest';
44
import { ElementDriver } from './ElementDriver';
55
import { PluginInterface } from './plugins/PluginInterface';
66
import BackendResponse from '../Backend/BackendResponse';
7-
export type ComponentHookName = 'connect' | 'disconnect' | 'request:started' | 'render:started' | 'render:finished' | 'response:error' | 'loading.state:started' | 'loading.state:finished' | 'model:set';
87
type MaybePromise<T = void> = T | Promise<T>;
8+
export type ComponentHooks = {
9+
'connect': (component: Component) => MaybePromise;
10+
'disconnect': (component: Component) => MaybePromise;
11+
'request:started': (requestConfig: any) => MaybePromise;
12+
'render:finished': (component: Component) => MaybePromise;
13+
'response:error': (backendResponse: BackendResponse, controls: {
14+
displayError: boolean;
15+
}) => MaybePromise;
16+
'loading.state.started': (element: HTMLElement, request: BackendRequest) => MaybePromise;
17+
'loading.state.finished': (element: HTMLElement) => MaybePromise;
18+
'model:set': (model: string, value: any, component: Component) => MaybePromise;
19+
};
20+
export type ComponentHookName = keyof ComponentHooks;
21+
export type ComponentHookCallback<T extends string = ComponentHookName> = T extends ComponentHookName ? ComponentHooks[T] : (...args: any[]) => MaybePromise;
922
export default class Component {
1023
readonly element: HTMLElement;
1124
readonly name: string;
@@ -33,34 +46,8 @@ export default class Component {
3346
addPlugin(plugin: PluginInterface): void;
3447
connect(): void;
3548
disconnect(): void;
36-
on(hookName: 'connect', callback: (component: Component) => MaybePromise): void;
37-
on(hookName: 'disconnect', callback: (component: Component) => MaybePromise): void;
38-
on(hookName: 'request:started', callback: (requestConfig: any) => MaybePromise): void;
39-
on(hookName: 'render:started', callback: (html: string, response: BackendResponse, controls: {
40-
shouldRender: boolean;
41-
}) => MaybePromise): void;
42-
on(hookName: 'render:finished', callback: (component: Component) => MaybePromise): void;
43-
on(hookName: 'response:error', callback: (backendResponse: BackendResponse, controls: {
44-
displayError: boolean;
45-
}) => MaybePromise): void;
46-
on(hookName: 'loading.state:started', callback: (element: HTMLElement, request: BackendRequest) => MaybePromise): void;
47-
on(hookName: 'loading.state:finished', callback: (element: HTMLElement) => MaybePromise): void;
48-
on(hookName: 'model:set', callback: (model: string, value: any, component: Component) => MaybePromise): void;
49-
on(hookName: string, callback: (...args: any[]) => MaybePromise): void;
50-
off(hookName: 'connect', callback: (component: Component) => MaybePromise): void;
51-
off(hookName: 'disconnect', callback: (component: Component) => MaybePromise): void;
52-
off(hookName: 'request:started', callback: (requestConfig: any) => MaybePromise): void;
53-
off(hookName: 'render:started', callback: (html: string, response: BackendResponse, controls: {
54-
shouldRender: boolean;
55-
}) => MaybePromise): void;
56-
off(hookName: 'render:finished', callback: (component: Component) => MaybePromise): void;
57-
off(hookName: 'response:error', callback: (backendResponse: BackendResponse, controls: {
58-
displayError: boolean;
59-
}) => MaybePromise): void;
60-
off(hookName: 'loading.state:started', callback: (element: HTMLElement, request: BackendRequest) => MaybePromise): void;
61-
off(hookName: 'loading.state:finished', callback: (element: HTMLElement) => MaybePromise): void;
62-
off(hookName: 'model:set', callback: (model: string, value: any, component: Component) => MaybePromise): void;
63-
off(hookName: string, callback: (...args: any[]) => MaybePromise): void;
49+
on<T extends string | ComponentHookName = ComponentHookName>(hookName: T, callback: ComponentHookCallback<T>): void;
50+
off<T extends string | ComponentHookName = ComponentHookName>(hookName: T, callback: ComponentHookCallback<T>): void;
6451
set(model: string, value: any, reRender?: boolean, debounce?: number | boolean): Promise<BackendResponse>;
6552
getData(model: string): any;
6653
action(name: string, args?: any, debounce?: number | boolean): Promise<BackendResponse>;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export default class {
22
private hooks;
3-
register(hookName: string, callback: () => void): void;
4-
unregister(hookName: string, callback: () => void): void;
3+
register(hookName: string, callback: (...args: any[]) => void): void;
4+
unregister(hookName: string, callback: (...args: any[]) => void): void;
55
triggerHook(hookName: string, ...args: any[]): void;
66
}

src/LiveComponent/assets/src/Component/index.ts

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,25 @@ import { findComponents, registerComponent, unregisterComponent } from '../Compo
1414

1515
declare const Turbo: any;
1616

17-
export type ComponentHookName = 'connect' | 'disconnect' | 'request:started' | 'render:started' | 'render:finished' | 'response:error' | 'loading.state:started' | 'loading.state:finished' | 'model:set';
18-
1917
type MaybePromise<T = void> = T | Promise<T>;
2018

19+
export type ComponentHooks = {
20+
'connect': (component: Component) => MaybePromise,
21+
'disconnect': (component: Component) => MaybePromise,
22+
'request:started': (requestConfig: any) => MaybePromise,
23+
'render:finished': (component: Component) => MaybePromise,
24+
'response:error': (backendResponse: BackendResponse, controls: { displayError: boolean }) => MaybePromise,
25+
'loading.state.started': (element: HTMLElement, request: BackendRequest) => MaybePromise,
26+
'loading.state.finished': (element: HTMLElement) => MaybePromise,
27+
'model:set': (model: string, value: any, component: Component) => MaybePromise,
28+
};
29+
30+
export type ComponentHookName = keyof ComponentHooks;
31+
32+
export type ComponentHookCallback<T extends string = ComponentHookName> = T extends ComponentHookName
33+
? ComponentHooks[T]
34+
: (...args: any[]) => MaybePromise;
35+
2136
export default class Component {
2237
readonly element: HTMLElement;
2338
readonly name: string;
@@ -113,44 +128,11 @@ export default class Component {
113128
this.externalMutationTracker.stop();
114129
}
115130

116-
/**
117-
* Add a named hook to the component. Available hooks are:
118-
*
119-
* * connect (component: Component) => {}
120-
* * disconnect (component: Component) => {}
121-
* * request:started (requestConfig: any) => {}
122-
* * render:started (html: string, response: BackendResponse, controls: { shouldRender: boolean }) => {}
123-
* * render:finished (component: Component) => {}
124-
* * response:error (backendResponse: BackendResponse, controls: { displayError: boolean }) => {}
125-
* * loading.state:started (element: HTMLElement, request: BackendRequest) => {}
126-
* * loading.state:finished (element: HTMLElement) => {}
127-
* * model:set (model: string, value: any, component: Component) => {}
128-
*/
129-
on(hookName: 'connect', callback: (component: Component) => MaybePromise): void;
130-
on(hookName: 'disconnect', callback: (component: Component) => MaybePromise): void;
131-
on(hookName: 'request:started', callback: (requestConfig: any) => MaybePromise): void;
132-
on(hookName: 'render:started', callback: (html: string, response: BackendResponse, controls: { shouldRender: boolean }) => MaybePromise): void;
133-
on(hookName: 'render:finished', callback: (component: Component) => MaybePromise): void;
134-
on(hookName: 'response:error', callback: (backendResponse: BackendResponse, controls: { displayError: boolean }) => MaybePromise): void;
135-
on(hookName: 'loading.state:started', callback: (element: HTMLElement, request: BackendRequest) => MaybePromise): void;
136-
on(hookName: 'loading.state:finished', callback: (element: HTMLElement) => MaybePromise): void;
137-
on(hookName: 'model:set', callback: (model: string, value: any, component: Component) => MaybePromise): void;
138-
on(hookName: string, callback: (...args: any[]) => MaybePromise): void;
139-
on(hookName: ComponentHookName, callback: (...args: any[]) => MaybePromise): void {
131+
on<T extends string | ComponentHookName = ComponentHookName>(hookName: T, callback: ComponentHookCallback<T>): void {
140132
this.hooks.register(hookName, callback);
141133
}
142134

143-
off(hookName: 'connect', callback: (component: Component) => MaybePromise): void;
144-
off(hookName: 'disconnect', callback: (component: Component) => MaybePromise): void;
145-
off(hookName: 'request:started', callback: (requestConfig: any) => MaybePromise): void;
146-
off(hookName: 'render:started', callback: (html: string, response: BackendResponse, controls: { shouldRender: boolean }) => MaybePromise): void;
147-
off(hookName: 'render:finished', callback: (component: Component) => MaybePromise): void;
148-
off(hookName: 'response:error', callback: (backendResponse: BackendResponse, controls: { displayError: boolean }) => MaybePromise): void;
149-
off(hookName: 'loading.state:started', callback: (element: HTMLElement, request: BackendRequest) => MaybePromise): void;
150-
off(hookName: 'loading.state:finished', callback: (element: HTMLElement) => MaybePromise): void;
151-
off(hookName: 'model:set', callback: (model: string, value: any, component: Component) => MaybePromise): void;
152-
off(hookName: string, callback: (...args: any[]) => MaybePromise): void;
153-
off(hookName: ComponentHookName, callback: (...args: any[]) => MaybePromise): void {
135+
off<T extends string | ComponentHookName = ComponentHookName>(hookName: T, callback: ComponentHookCallback<T>): void {
154136
this.hooks.unregister(hookName, callback);
155137
}
156138

src/LiveComponent/assets/src/HookManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
export default class {
22
private hooks: Map<string, Array<(...args: any[]) => void>> = new Map();
33

4-
register(hookName: string, callback: () => void): void {
4+
register(hookName: string, callback: (...args: any[]) => void): void {
55
const hooks = this.hooks.get(hookName) || [];
66
hooks.push(callback);
77
this.hooks.set(hookName, hooks);
88
}
99

10-
unregister(hookName: string, callback: () => void): void {
10+
unregister(hookName: string, callback: (...args: any[]) => void): void {
1111
const hooks = this.hooks.get(hookName) || [];
1212
const index = hooks.indexOf(callback);
1313
if (index === -1) {

0 commit comments

Comments
 (0)