Skip to content

Instantly share code, notes, and snippets.

@disco0
Last active October 19, 2020 22:54
Show Gist options
  • Save disco0/94a89973c29ce26305a3906e6fce1ae3 to your computer and use it in GitHub Desktop.
Save disco0/94a89973c29ce26305a3906e6fce1ae3 to your computer and use it in GitHub Desktop.
TypeScript - Indent control method decorator
/**
* NOTE: Use browser console
*
* @playground <https://tsplay.dev/KwXl8w>
*/
//#region Declarations
export type NaturalNumber = Number & Partial<{__brand?: null}>
export interface Indentable
{
indent?: NaturalNumber;
// indent?: number;
}
type TFunction = (...args: any[]) => any;
//#endregion Declarations
//#region Util/Vars
function isNaturalNumber(obj: unknown): obj is NaturalNumber
{
return (typeof obj === 'number' && Number.isInteger(obj) && obj >= 0)
}
export function assertIndentLevelIsNatural(obj: unknown): asserts obj is NaturalNumber
{
if(isNaturalNumber(obj)) return;
throw new Error('Indent value must be a natural number (ℕ₀).')
}
export function indentAmountToString(char: string, level: number): string;
export function indentAmountToString(char: string, level: NaturalNumber): string
export function indentAmountToString(char: string = base.char, level: NaturalNumber | number): string
{ return char.repeat(level as number) };
export const base =
{
char: ' ',
amount: 0,
increment: 2
} as const;
export function bakeIndentInMethod<
T extends Indentable,
F extends (...args: any[]) => any
>(
originalFunction: F,
level: number
): F {
return function (this: T, ...args: Parameters<F>)
{
const initial = this.indent!;
this.indent = level;
return (returned => {
this.indent = initial;
return returned;
})(originalFunction.apply(this, args))
} as F;
}
//#endregion Util/Vars
//#region Decorator
export function indent<C extends Indentable>(level: number)
{
assertIndentLevelIsNatural(level)
return function(target: Object, propertyKey: string, descriptor: any & {value: any})
{
const originalMethod = descriptor.value; // save a reference to the original method
// NOTE: Do not use arrow syntax here. Use a function expression in
// order to use the correct value of `this` in this method (see notes below)
Object.assign(
descriptor,
{value: bakeIndentInMethod<C, typeof originalMethod>(originalMethod, level)} )
return descriptor;
}
}
//#endregion Decorator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment