-
-
Save Ivajkin/c6c6351a6e667673fa752dd74a681843 to your computer and use it in GitHub Desktop.
TypeScript contract-based decorators
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
| type MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => | |
| TypedPropertyDescriptor | void; | |
| type AssertFn = (...args: Array<any>) => void; | |
| class TSContract { | |
| private static isCustomContractInterruptionCb: boolean = false; | |
| private static contractInterruptionCb(message: string): void { | |
| console.warn(message); | |
| } | |
| static setupContractInterruptionCb(cb: (message: string) => void): void { | |
| if (TSContract.isCustomContractInterruptionCb) { | |
| console.warn('Custom contract interruption callback already setted, break'); | |
| return; | |
| } | |
| TSContract.contractInterruptionCb = cb; | |
| TSContract.isCustomContractInterruptionCb = true; | |
| } | |
| static assert(expression: boolean, errorMessage: string): void { | |
| if (!expression) { | |
| TSContract.contractInterruptionCb(errorMessage); | |
| } | |
| } | |
| static In(assertFn: AssertFn): MethodDecorator { | |
| return function(target: any, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor { | |
| const originalValue = descriptor.value; | |
| function newValue(...args) { | |
| assertFn(...args); | |
| return originalValue.apply(this, args); | |
| } | |
| descriptor.value = newValue; | |
| return descriptor; | |
| }; | |
| } | |
| static Out(assertFn: AssertFn): MethodDecorator { | |
| return function(target: any, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor { | |
| const originalValue = descriptor.value; | |
| function newValue(...args) { | |
| result = originalValue.apply(this, args); | |
| assertFn(result); | |
| return result; | |
| } | |
| descriptor.value = newValue; | |
| return descriptor; | |
| }; | |
| } | |
| } | |
| class ContractTest { | |
| @TSContract.In(a => { | |
| TSContract.assert(typeof a === 'number', 'Not a number!'); | |
| }) | |
| @TSContract.Out(retVal => { | |
| TSContract.assert(!retVal, 'Wat?! Return?!'); | |
| }) | |
| doSome(a: number, withReturn: boolean = false): void { | |
| console.log(a); | |
| if (withReturn) { | |
| return a; | |
| } | |
| } | |
| } | |
| TSContract.setupContractInterruptionCb((message: string) => { | |
| console.error(message); | |
| }); | |
| // cant setup another callback | |
| TSContract.setupContractInterruptionCb((message: string) => { | |
| console.error(message); | |
| }); | |
| const ct = new ContractTest(); | |
| ct.doSome(12); | |
| ct.doSome('12'); | |
| ct.doSome(12, true); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment