Skip to content

Commit c2e1e22

Browse files
committed
feat!: improve type checking
1 parent ae9e030 commit c2e1e22

File tree

3 files changed

+42
-32
lines changed

3 files changed

+42
-32
lines changed

src/hookable.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { serial, flatHooks, mergeHooks } from './utils'
2-
import { LoggerT, hookFnT, configHooksT, deprecatedHookT, deprecatedHooksT } from './types'
2+
import type { LoggerT, DeprecatedHook, NestedHooks, HookCallback, HookKeys } from './types'
33
export * from './types'
44

5-
class Hookable {
6-
private _hooks: { [name: string]: hookFnT[] }
7-
private _deprecatedHooks: deprecatedHooksT
5+
class Hookable <
6+
_HooksT = Record<string, HookCallback>,
7+
HooksT = _HooksT & { error: (error: Error | any) => void },
8+
HookNameT extends HookKeys<HooksT> = HookKeys<HooksT>
9+
> {
10+
private _hooks: { [key: string]: HookCallback[] }
11+
private _deprecatedHooks: Record<string, DeprecatedHook<HooksT>>
812
private _logger: LoggerT | false
913

1014
static mergeHooks: typeof mergeHooks
@@ -20,7 +24,7 @@ class Hookable {
2024
this.callHook = this.callHook.bind(this)
2125
}
2226

23-
hook (name: string, fn: hookFnT) {
27+
hook <NameT extends HookNameT>(name: NameT, fn: HooksT[NameT] & HookCallback) {
2428
if (!name || typeof fn !== 'function') {
2529
return () => {}
2630
}
@@ -56,19 +60,19 @@ class Hookable {
5660
}
5761
}
5862

59-
hookOnce (name: string, fn: hookFnT) {
63+
hookOnce <NameT extends HookNameT>(name: NameT, fn: HooksT[NameT] & HookCallback) {
6064
let _unreg
6165
let _fn = (...args) => {
6266
_unreg()
6367
_unreg = null
6468
_fn = null
6569
return fn(...args)
6670
}
67-
_unreg = this.hook(name, _fn)
71+
_unreg = this.hook(name, _fn as typeof fn)
6872
return _unreg
6973
}
7074

71-
removeHook (name: string, fn: hookFnT) {
75+
removeHook <NameT extends HookNameT> (name: NameT, fn: HooksT[NameT] & HookCallback) {
7276
if (this._hooks[name]) {
7377
const idx = this._hooks[name].indexOf(fn)
7478

@@ -82,16 +86,17 @@ class Hookable {
8286
}
8387
}
8488

85-
deprecateHook (name: string, deprecated: deprecatedHookT) {
89+
deprecateHook <NameT extends HookNameT> (name: NameT, deprecated: DeprecatedHook<HooksT>) {
8690
this._deprecatedHooks[name] = deprecated
8791
}
8892

89-
deprecateHooks (deprecatedHooks: deprecatedHooksT) {
93+
deprecateHooks (deprecatedHooks: Record<HookNameT, DeprecatedHook<HooksT>>) {
9094
Object.assign(this._deprecatedHooks, deprecatedHooks)
9195
}
9296

93-
addHooks (configHooks: configHooksT) {
94-
const hooks = flatHooks(configHooks)
97+
addHooks (configHooks: NestedHooks<HooksT>) {
98+
const hooks = flatHooks<HooksT>(configHooks)
99+
// @ts-ignore
95100
const removeFns = Object.keys(hooks).map(key => this.hook(key, hooks[key]))
96101

97102
return () => {
@@ -101,21 +106,24 @@ class Hookable {
101106
}
102107
}
103108

104-
removeHooks (configHooks: configHooksT) {
105-
const hooks = flatHooks(configHooks)
109+
removeHooks (configHooks: NestedHooks<HooksT>) {
110+
const hooks = flatHooks<HooksT>(configHooks)
106111
for (const key in hooks) {
112+
// @ts-ignore
107113
this.removeHook(key, hooks[key])
108114
}
109115
}
110116

111-
async callHook (name: string, ...args: any) {
117+
// @ts-ignore HooksT[NameT] & HookCallback prevents typechecking
118+
async callHook <NameT extends HookNameT> (name: NameT, ...args: Parameters<HooksT[NameT]>) {
112119
if (!this._hooks[name]) {
113120
return
114121
}
115122
try {
116123
await serial(this._hooks[name], fn => fn(...args))
117124
} catch (err) {
118125
if (name !== 'error') {
126+
// @ts-ignore Stranger Things
119127
await this.callHook('error', err)
120128
}
121129
if (this._logger) {

src/types.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
export type unregHookT = () => void
2-
export type hookFnT = (...args: any) => Promise<void> | void
3-
export type configHooksT = { [name: string]: configHooksT | hookFnT }
4-
export type deprecatedHookT = string | { message: string, to: string }
5-
export type deprecatedHooksT = { [name: string]: deprecatedHookT}
6-
export type flatHooksT = { [name: string]: hookFnT }
1+
export type HookCallback = (...args: any) => Promise<void> | void
2+
3+
export interface Hooks { [key: string]: HookCallback }
4+
export type HookKeys<T> = keyof T & string
5+
export type NestedHooks<T> = { [name in HookKeys<T>]: NestedHooks<T> | HookCallback }
6+
export type DeprecatedHook<T> = string | { message: string, to: HookKeys<T> }
7+
export type DeprecatedHooks<T> = { [name in HookKeys<T>]: DeprecatedHook<T> }
78

89
export interface LoggerT {
910
error(...args: any): void,

src/utils.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
1-
import { configHooksT, flatHooksT } from './types'
1+
import { NestedHooks } from './types'
22

3-
export function flatHooks (configHooks: configHooksT, hooks: flatHooksT = {}, parentName?: string): flatHooksT {
3+
export function flatHooks<T> (configHooks: NestedHooks<T>, hooks: T = {} as T, parentName?: string): T {
44
for (const key in configHooks) {
55
const subHook = configHooks[key]
66
const name = parentName ? `${parentName}:${key}` : key
77
if (typeof subHook === 'object' && subHook !== null) {
88
flatHooks(subHook, hooks, name)
99
} else if (typeof subHook === 'function') {
10+
// @ts-ignore
1011
hooks[name] = subHook
1112
}
1213
}
13-
return hooks
14+
return hooks as any
1415
}
1516

16-
export function mergeHooks (...hooks: configHooksT[]): flatHooksT {
17-
const finalHooks: any = {}
17+
export function mergeHooks<T> (...hooks: NestedHooks<T>[]): T {
18+
const finalHooks = {} as any
1819

19-
for (let _hook of hooks) {
20-
_hook = flatHooks(_hook)
21-
for (const key in _hook) {
20+
for (let hook of hooks) {
21+
const flatenHook = flatHooks(hook)
22+
for (const key in flatenHook) {
2223
if (finalHooks[key]) {
23-
finalHooks[key].push(_hook[key])
24+
finalHooks[key].push(flatenHook[key])
2425
} else {
25-
finalHooks[key] = [_hook[key]]
26+
finalHooks[key] = [flatenHook[key]]
2627
}
2728
}
2829
}
@@ -36,7 +37,7 @@ export function mergeHooks (...hooks: configHooksT[]): flatHooksT {
3637
}
3738
}
3839

39-
return finalHooks
40+
return finalHooks as any
4041
}
4142

4243
export function serial<T> (tasks: T[], fn: (task: T) => Promise<any> | any) {

0 commit comments

Comments
 (0)