I have the following interface for HTTP response
interface HTTPResponse<T> {
ok: boolean,
error?: HTTPError;
data?: T,
init: ResponseInit
}
and this type narrowing check
export function isHTTPError(object: any): object is HTTPError {
return "error" in object && object.ok === false;
}
when I pass a response through the isHTTPError
check, typescript can tell if this is an error response and knows that error
can't be undefined at this point.
If the response is not an error how to tell typescript that data
is definitely present and should not be treated as possibly undefined?
At this moment I am using the following function
export function isHTTPSuccess<T>(object: any): object is T {
return "data" in object && object.ok === true;
}
but this requires checking the response twice.
CodePudding user response:
You want a discriminated union, where ok
is the discriminant.
You need two types. One for the success case, and another for the error case.
interface HTTPResponseSuccess<T> {
ok: true
error?: undefined
data: T
init: ResponseInit
}
interface HTTPResponseError {
ok: false
error: HTTPError
data?: undefined
init: ResponseInit
}
These have the same properties, but different types. This will, for example, allow typescript to know that if ok
is true
then there is no error and data exists.
Now you can build a union of these types to use when you don't know the result.
type HTTPResponse<T> = HTTPResponseSuccess<T> | HTTPResponseError
And now you can simply do:
// mock a response where you dont yet know if it was a success.
const res: HTTPResponse<number> = await someApi.getSomething()
if (res.ok) {
console.log(res.data.toFixed())
}