Skip to content

Instantly share code, notes, and snippets.

@larrynung
Forked from SoEasy/tscontract.ts
Created April 3, 2018 07:04
Show Gist options
  • Save larrynung/38fee67c79c09db5fec580bb7f8ee7a8 to your computer and use it in GitHub Desktop.
Save larrynung/38fee67c79c09db5fec580bb7f8ee7a8 to your computer and use it in GitHub Desktop.
TypeScript contract-based decorators
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