hooks.ts 2.75 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,
  K extends string | symbol
  >(target: T, name: K, hook: M): void {
  installHook(
    target,
    name,
    oldMethod =>
      function(...args: any[]): void {
7172737475767778798081828384858687888990919293949596979899100101102
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); } } ); }