hooks.ts 2.76 KiB
/**
 * Fonctions pour "patcher" les méthodes d'une classe.
 */
type Hookable<K extends string | symbol, M extends (...args: any[]) => any> = {
  [X in K]: M
};
function isHookable<
  K extends string | symbol,
  M extends (...args: any[]) => any
>(what: unknown, key: K): what is Hookable<K, M> {
  return (
    typeof what === 'object' &&
    what !== null &&
    key in what &&
    typeof (what as any)[key] === 'function'
/**
 * Installe un hook.
function installHook<
  T extends Hookable<K, M>,
  M extends (this: T, ...args: any[]) => void,
  K extends string | symbol
>(target: T, name: K, makeHook: (old: M) => M): void {
  if (!isHookable<K, M>(target, name)) {
    throw new Error(`cannot hook ${name} on ${target}`);
  if (name === 'constructor') {
    throw new Error('cannot hook constructors');
  target[name] = <T[K]>makeHook(target[name]);
/**
 * Modifie une méthode d'un objet pour éxecuter une fonction avant son éxecution normale.
 * @param {object} target Le prototype à modifier.
 * @param {string|symbol} name Le nom de la méthode à surcharger.
 * @param {function} hook La fonction à ajouter.
export function hookBefore<
  T extends Hookable<K, M>,
  M extends (this: T, ...args: any[]) => void,
  K extends string | symbol
>(target: Hookable<K, M>, name: K, hook: M): void {
  installHook(
    target,
    name,
    oldMethod =>
      function(...args: any[]): void {
        hook.apply(this, args);
        oldMethod.apply(this, args);
/**
 * Modifie une méthode d'un objet pour éxecuter une fonction après son éxecution normale.
 * @param {object} target Le prototype à modifier.
 * @param {string|symbol} name Le nom de la méthode à surcharger.
 * @param {function} hook La fonction à ajouter.
export function hookAfter<
  T extends Hookable<K, M>,
  M extends (this: T, ...args: any[]) => void,
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
K extends string | symbol >(target: T, name: K, hook: M): void { installHook( target, name, oldMethod => function(...args: any[]): void { oldMethod.apply(this, args); hook.apply(this, args); } ); } /** * Modifie une méthode d'un objet pour éxecuter inconditionnellement une fonction après son éxecution normale. * * @param {object} target Le prototype à modifier. * @param {string|symbol} name Le nom de la méthode à surcharger. * @param {function} hook La fonction à ajouter. */ export function hookFinally< T extends Hookable<K, M>, M extends (this: T, ...args: any[]) => void, K extends string | symbol >(target: T, name: K, hook: M): void { installHook( target, name, oldMethod => function(...args: any[]): void { try { oldMethod.apply(this, args); } finally { hook.apply(this, args); } } ); }