Skip to content

Instantly share code, notes, and snippets.

@JonDotsoy
Last active May 22, 2024 03:07
Show Gist options
  • Save JonDotsoy/c13ef06ccc84c50474222c51cf33f6e7 to your computer and use it in GitHub Desktop.
Save JonDotsoy/c13ef06ccc84c50474222c51cf33f6e7 to your computer and use it in GitHub Desktop.
A Javascript utils to decorate functions

Decorate

Example

const increase = decorate(
  (n: number) => n,
  increaseBy1,
  increaseBy2,
  increaseBy3,
);

await increase(1) // => 7

Decorator

const increaseBy1 = async function (descriptor: (n: number) => Promise<number>) {
  return async function (n: number) {
    return await descriptor(n + 1);
  };
};
type Promised<R> = R | PromiseLike<R>;
export type Descriptor<A extends any[], R> = (...args: A) => Promised<R>;
export type Decorator<A extends any[], R> = (
descriptor: Descriptor<A, R>,
) => Promised<Descriptor<A, R>>;
export type DescriptorSync<A extends any[], R> = (...args: A) => R;
export type DecoratorSync<A extends any[], R> = (
descriptor: DescriptorSync<A, R>,
) => DescriptorSync<A, R>;
/**
* @example
* const decorated = decorate(n => n, increaseBy1, increaseBy2, increaseBy3);
* decorated(1); // => 7
* @param descriptor
* @param decorators
* @returns a new descriptor wrapped
*/
export const decorate = <A extends any[], R>(
descriptor: Descriptor<A, R>,
...decorators: Decorator<A, R>[]
) => {
return async function (this: unknown, ...args: A): Promise<R> {
const newDescriptor = decorators.reduce(
(
descriptor: Descriptor<A, R>,
decorator: Decorator<A, R>,
): Descriptor<A, R> => {
return async (...args: A): Promise<R> => {
const nextDescriptor = await Promise.resolve(
decorator.call(this, descriptor),
);
return await Promise.resolve(nextDescriptor.call(this, ...args));
};
},
descriptor.bind(this),
);
return await newDescriptor(...args);
};
};
/**
* @example
* const decorated = decorate(n => n, increaseBy1, increaseBy2, increaseBy3);
* decorated(1); // => 7
* @param descriptor
* @param decorators
* @returns a new descriptor wrapped
*/
export const decorateSync = <A extends any[], R>(
descriptor: DescriptorSync<A, R>,
...decorators: DecoratorSync<A, R>[]
) => {
return function (this: unknown, ...args: A): R {
const newDescriptor = decorators.reduce(
(
descriptor: DescriptorSync<A, R>,
decorator: DecoratorSync<A, R>,
): DescriptorSync<A, R> => {
return (...args: A): R => {
const nextDescriptor = decorator.call(this, descriptor);
return nextDescriptor.call(this, ...args);
};
},
descriptor.bind(this),
);
return newDescriptor(...args);
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment