Consider this context: An API auth route which I don't have access to view or edit the code returns
interface AuthSuccess {
token: string
user: object
}
on response.data if email and password are correct, but returns
interface AuthError {
error: string
}
on error.response.data if they aren't correct or aren't passed in the body.
On my front-end, built in NextJS with axios to make requests, I've created the following function to call this route:
const auth = ({email, password}) =>
api // api is the return of axios.create()
.post(url, {email, password})
.then((result) => ({
success: true,
data: response.data // here it it AuthSuccess
}))
.catch((error) => ({
success: false,
data: error.response?.data // here it is AuthError
}))
However, when I call this function like this
const res = await auth(data)
if (res.success) {
// here I expect data to be AuthSuccess type
} else {
// here I expect data to be AuthError type
}
Even if I'm verifying res.success
, inside the if block Typescript acts like data can have both types, which it can't, obviously.
I've tried 2 solutions so far:
Setting auth
type as (arg: AuthProps): Promise<AuthSuccess | AuthError>
, which creates the problem with the if block
Creating another type, conditionally:
interface AuthResult<T extends boolean> {
success: T
data: T extends true ? AuthSuccess : AuthError
}
And setting auth
to be (arg: AuthProps): Promise<AuthResult>
, to which TS complains about not passing the generic, and at the if block everything becomes any
.
So, how can I type this function correctly? Or how can I refactor it to stop the problem? Ty in advance
You can use a union type:
interface SuccessResponse {
success: true;
data: {
token: string;
user: object;
};
}
interface ErrorResponse {
success: false;
data: {
error: string;
};
}
type AuthResponse = SuccessResponse | ErrorResponse;
const test = (res: AuthResponse) => {
if (res.success) {
return res.data; // { token: string; user: object; }
} else {
return res.data; // { error: string; }
}
}
Typescript playground
Or a function that returns a type predicate:
interface SuccessResponse {
success: true;
data: {
token: string;
user: object;
};
}
interface ErrorResponse {
success: false;
data: {
error: string;
};
}
const isSuccessResponse = (res: SuccessResponse | ErrorResponse): res is SuccessResponse => {
return res.success;
}
const test = (res: SuccessResponse | ErrorResponse) => {
if (isSuccessResponse(res)) {
return res.data; // { token: string; user: object; }
} else {
return res.data; // { error: string; }
}
}
Typescript playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With