import { MongooseDocument } from 'mongoose'; import { hooks as hooksData } from './data'; type DocumentMethod = 'init' | 'validate' | 'save' | 'remove'; type QueryMethod = 'count' | 'find' | 'findOne' | 'findOneAndRemove' | 'findOneAndUpdate' | 'update' | 'updateOne'; type ModelMethod = 'insertMany'; type ClassDecorator = (constructor: any) => void; type HookNextFn = (err?: Error) => void; type PreDoneFn = () => void; type TypegooseDoc = T & MongooseDocument; type DocumentPreSerialFn = (this: TypegooseDoc, next: HookNextFn) => void; type DocumentPreParallelFn = (this: TypegooseDoc, next: HookNextFn, done: PreDoneFn) => void; type SimplePreSerialFn = (next: HookNextFn) => void; type SimplePreParallelFn = (next: HookNextFn, done: PreDoneFn) => void; type DocumentPostFn = (this: TypegooseDoc, doc: TypegooseDoc, next?: HookNextFn) => void; type ModelPostFn = (result: any, next?: HookNextFn) => void; type PostNumberResponse = (result: number, next?: HookNextFn) => void; type PostSingleResponse = (result: TypegooseDoc, next?: HookNextFn) => void; type PostMultipleResponse = (result: TypegooseDoc[], next?: HookNextFn) => void; type PostNumberWithError = (error: Error, result: number, next: HookNextFn) => void; type PostSingleWithError = (error: Error, result: TypegooseDoc, next: HookNextFn) => void; type PostMultipleWithError = (error: Error, result: TypegooseDoc[], net: HookNextFn) => void; type NumberMethod = 'count'; type SingleMethod = 'findOne' | 'findOneAndRemove' | 'findOneAndUpdate' | DocumentMethod; type MultipleMethod = 'find' | 'update'; interface Hooks { pre(method: DocumentMethod, fn: DocumentPreSerialFn): ClassDecorator; pre(method: DocumentMethod, parallel: boolean, fn: DocumentPreParallelFn): ClassDecorator; pre(method: QueryMethod | ModelMethod, fn: SimplePreSerialFn): ClassDecorator; pre( method: QueryMethod | ModelMethod, parallel: boolean, fn: SimplePreParallelFn): ClassDecorator; // I had to disable linter to allow this. I only got proper code completion separating the functions post(method: NumberMethod, fn: PostNumberResponse): ClassDecorator; // tslint:disable-next-line:unified-signatures post(method: NumberMethod, fn: PostNumberWithError): ClassDecorator; post(method: SingleMethod, fn: PostSingleResponse): ClassDecorator; // tslint:disable-next-line:unified-signatures post(method: SingleMethod, fn: PostSingleWithError): ClassDecorator; post(method: MultipleMethod, fn: PostMultipleResponse): ClassDecorator; // tslint:disable-next-line:unified-signatures post(method: MultipleMethod, fn: PostMultipleWithError): ClassDecorator; post(method: ModelMethod, fn: ModelPostFn | PostMultipleResponse): ClassDecorator; } const hooks: Hooks = { pre(...args) { return (constructor: any) => { addToHooks(constructor.name, 'pre', args); }; }, post(...args) { return (constructor: any) => { addToHooks(constructor.name, 'post', args); }; }, }; const addToHooks = (name, hookType: 'pre' | 'post', args) => { if (!hooksData[name]) { hooksData[name] = { pre: [], post: [] }; } hooksData[name][hookType].push(args); }; export const pre = hooks.pre; export const post = hooks.post;