Home > Net >  typescript type narrowing `data` is not undefined when `error` is undefined
typescript type narrowing `data` is not undefined when `error` is undefined

Time:11-02

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())
}

See playground

  • Related