英文:
Typescript: How to correctly type response.data on front-end side based on success of the request?
问题
考虑到这个上下文:一个API授权路由,我无法查看或编辑其代码,如果电子邮件和密码正确,它会在响应数据中返回以下内容:
interface AuthSuccess {
token: string
user: object
}
但如果它们不正确或未传递在请求体中,它会返回以下内容:
interface AuthError {
error: string
}
在我的前端应用中,使用NextJS构建,使用axios进行请求,我创建了以下函数来调用这个路由:
const auth = ({ email, password }) =>
api // api是axios.create()的返回值
.post(url, { email, password })
.then((result) => ({
success: true,
data: response.data // 这里是AuthSuccess
}))
.catch((error) => ({
success: false,
data: error.response?.data // 这里是AuthError
}))
然而,当我像这样调用这个函数时:
const res = await auth(data)
if (res.success) {
// 这里我期望数据是AuthSuccess类型
} else {
// 这里我期望数据是AuthError类型
}
即使我在验证res.success
,在if块内部,TypeScript会认为数据可以具有两种类型,这显然是不对的。
到目前为止,我尝试了两种解决方案:
1)将auth
的类型设置为(arg: AuthProps): Promise<AuthSuccess | AuthError>
,这会导致if块的问题。
2)创建另一种类型,有条件地:
interface AuthResult<T extends boolean> {
success: T
data: T extends true ? AuthSuccess : AuthError
}
并将auth
设置为(arg: AuthProps): Promise<AuthResult>
,但TypeScript会投诉没有传递泛型,而在if块中一切都变成了any
。
那么,如何正确为这个函数定义类型?或者如何重构它以解决这个问题?提前感谢您。
英文:
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
答案1
得分: 2
你可以使用联合类型:
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; }
}
}
或者使用返回类型谓词的函数:
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; }
}
}
英文:
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; }
}
}
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; }
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论