@@ -1,10 +1,14 @@
11import { serial, flatHooks, mergeHooks } from './utils'
2-import { LoggerT, hookFnT, configHooksT, deprecatedHookT, deprecatedHooksT } from './types'
2+import type { LoggerT, DeprecatedHook, NestedHooks, HookCallback, HookKeys } from './types'
33export * from './types'
445-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>>
812private _logger: LoggerT | false
9131014static mergeHooks: typeof mergeHooks
@@ -20,7 +24,7 @@ class Hookable {
2024this.callHook = this.callHook.bind(this)
2125}
222623-hook (name: string, fn: hookFnT) {
27+hook <NameT extends HookNameT>(name: NameT, fn: HooksT[NameT] & HookCallback) {
2428if (!name || typeof fn !== 'function') {
2529return () => {}
2630}
@@ -56,19 +60,19 @@ class Hookable {
5660}
5761}
586259-hookOnce (name: string, fn: hookFnT) {
63+hookOnce <NameT extends HookNameT>(name: NameT, fn: HooksT[NameT] & HookCallback) {
6064let _unreg
6165let _fn = (...args) => {
6266_unreg()
6367_unreg = null
6468_fn = null
6569return fn(...args)
6670}
67-_unreg = this.hook(name, _fn)
71+_unreg = this.hook(name, _fn as typeof fn)
6872return _unreg
6973}
707471-removeHook (name: string, fn: hookFnT) {
75+removeHook <NameT extends HookNameT> (name: NameT, fn: HooksT[NameT] & HookCallback) {
7276if (this._hooks[name]) {
7377const idx = this._hooks[name].indexOf(fn)
7478@@ -82,16 +86,17 @@ class Hookable {
8286}
8387}
848885-deprecateHook (name: string, deprecated: deprecatedHookT) {
89+deprecateHook <NameT extends HookNameT> (name: NameT, deprecated: DeprecatedHook<HooksT>) {
8690this._deprecatedHooks[name] = deprecated
8791}
889289-deprecateHooks (deprecatedHooks: deprecatedHooksT) {
93+deprecateHooks (deprecatedHooks: Record<HookNameT, DeprecatedHook<HooksT>>) {
9094Object.assign(this._deprecatedHooks, deprecatedHooks)
9195}
929693-addHooks (configHooks: configHooksT) {
94-const hooks = flatHooks(configHooks)
97+addHooks (configHooks: NestedHooks<HooksT>) {
98+const hooks = flatHooks<HooksT>(configHooks)
99+// @ts-ignore
95100const removeFns = Object.keys(hooks).map(key => this.hook(key, hooks[key]))
9610197102return () => {
@@ -101,21 +106,24 @@ class Hookable {
101106}
102107}
103108104-removeHooks (configHooks: configHooksT) {
105-const hooks = flatHooks(configHooks)
109+removeHooks (configHooks: NestedHooks<HooksT>) {
110+const hooks = flatHooks<HooksT>(configHooks)
106111for (const key in hooks) {
112+// @ts-ignore
107113this.removeHook(key, hooks[key])
108114}
109115}
110116111-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]>) {
112119if (!this._hooks[name]) {
113120return
114121}
115122try {
116123await serial(this._hooks[name], fn => fn(...args))
117124} catch (err) {
118125if (name !== 'error') {
126+// @ts-ignore Stranger Things
119127await this.callHook('error', err)
120128}
121129if (this._logger) {