Philip
Philip

Reputation: 75

Is it possible to deal with optional properties that are conditional based on a boolean in the type?

My current situation is a server response with a boolean success and optional response data or error information.

type ServerResponse = {
  success: boolean;
  data?: { [key: string]: string };
  err?: { code: number, message: string };
}

When I want to deal with this type, it can be a bit awkward:

const resp: ServerResponse = await fetch(...);

if(resp.success) {
   doSomething( resp.data?.foo );
} else {
   handleErr( resp.err?.message );
}

It's annoying to have to use the ? when I know that data will always be there when success === true and err will always be there when success === false

I've been trying to grok the documentation on mapped types, but am not finding what I need.

Is the only or best way to do this by changing the approach to ignore success?

const resp: ServerResponse = await fetch(...);

if(resp.data) {
   doSomething( resp.data.foo );
} else if(resp.err) {
   handleErr( resp.err.message );
}

While that seems reasonable, I'd like to learn more advanced ways of typing anyway.

Upvotes: 0

Views: 354

Answers (1)

Yulric Sequeira
Yulric Sequeira

Reputation: 1452

You can model this using union types.

The code for your use case is shown below,

interface SuccessResponse {
    success: true;
    data: { [key: string]: string }
}

interface ErrorResponse {
    success: false;
    err: { code: number, message: string };
}

type ServerResponse = SuccessResponse | ErrorResponse;

fetch("http://your-endpoint.com")
    .then((response) => {
        return response.json();
    })
    .then((jsonResponse: ServerResponse) => {
        if(jsonResponse.success) {
            console.log(jsonResponse.data)
        } else {
            console.log(jsonResponse.err)
        }
    })

TS Playground Version

Let me know if that's not clear.

Upvotes: 1

Related Questions