Last active
October 19, 2020 22:54
-
-
Save disco0/94a89973c29ce26305a3906e6fce1ae3 to your computer and use it in GitHub Desktop.
TypeScript - Indent control method decorator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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