I recently extracted and released
json method of the
ObsResponse class returns TypeScript
unknown, rather than the much more common
unknown instead of
any here in TypeScript may make the API more awkward to use, so I wanted to explain why I made this decision.
unknown makes the API more reliable
While this may make the API slightly more awkward, it provides a much sounder typing for your APIs. With
any, you have no guarantee that any of those API values are what TypeScript thinks they are. This pushes your errors away from their source, as they start appearing when you rely on guarantees you don’t have. This shows up in your error logs as
property of 'undefined' errors and can be very difficult to debug. API responses aren’t the first place you will look unless they’re really close to the location of the error.
If the response body is
unknown, TypeScript either forces you to validate it before you can do anything with it or explicitly cast it to
any. For the former case, you can use a library like
runtypes to validate your
unknown types to concrete ones (I’m currently using the former). Both of these packages accept an
unknown API response and provide either a validated, strictly typed API response, or an error, which you can then handle as you choose.
What if you don’t want to validate?
If neither of those work for you, you can cast to
any and get on with your life. You’ve explicitly opted into
any, rather than having
kefir-ajax introduce that for you. Later on, when you decide to introduce strict type validation to you API responses, you can easily find where it’s needed. Search your codebase for
any and wrap those with your new API validations.
Kefir in particular makes this really nice, because you can push any errors down Kefir’s error channel:
ajax$(...) .flatMap(response => response.json()) .flatMap(body => // body is `unknown` ResponseType.decode(body).fold<Observable<ResponseType, t.Error>>( Kefir.constantError, Kefir.constant, ) )
The error type gets added to the downstream type, and TypeScript warns you if you haven’t handled it,
t.Error. TypeScript’s compiler helps you introduce the validation without adding new bugs at the same time.
But even if you don’t use Kefir, or
unknown for your API responses then validate them. This helps you build trust in what TypeScript is telling you and keeps your errors closer to their cause.