I'm creating a TS wrapper over a JS embedded to third-party software API which is unmanaged by me. I want to capture as much behavior as even possible in types and interfaces because untyped vanilla JS is pretty hard to get it working in my case.
So, to be more specific:
I have external object and I have an interface which declares functions it has
declare var foo: object
interface FooContract {
function doFooStuff: (fooParam: string) => BlaError | Success
function doBarStuff: (barParam: string) => BlaError | Success
}
I want to check that:
- given object
foo
has fieldsdoFooStuff
anddoBarStuff
. - these fields are functions.
- these functions take params I described, if not return meaningful error message, at least check arity.
- these functions return data which is described in return type, if not return meaningful error message.
So I actually do understand that 3) and 4) pretty much runtime checks and should be verified by testing, but anyway.
Also sometimes API throws some errors, and I want to describe and signal somehow which error would be returned. Errors mostly strings and POJOs, and i want to assign types to them (preferably nominal ones, at cost of any number of wheel reinventions and dirty hacks).
So far, I came to:
declare var foo: object
interface FooContract {
function doFooStuff: (fooParam: string) => BlaError | Success
function doBarStuff: (barParam: string) => BlaError | Success
}
function isFooContract(x: object): x is FooContract {
const r = x as FooContract
return r.doFooStuff !== undefined && (typeof r.doFooStuff) == "function" &&
r.doBarStuff && (typeof r.doFooStuff) == "function"
}
class FooOps {
private fooInternal: FooContract
constructor (_inner: object) {
if(isFooContract(_inner))
this.fooInternal = _inner
else
throw new EverythingIsBrokenException(_inner, "foo object not satisfies listed asserts")
}
function doFooStuff(fooParam: string): BlaError | Success {
const ret = this.fooInternal(fooParam)
if(isBlaError(ret))
return ret
else if (isSuccess)
return ret
else
throw new EverythingIsBrokenException(ret, "Returned value of Foo.doFooStuff is not error nor success but something unexpected")
}
}
So there's no 3) at all, and immense amount boilerplate I need to handwrite. Is there kind of codegen/compiler macroextension/compile-time reflection tools to do the stuff for checking existence of fields?