Generic Support for Response Data
# Generic Support for Response Data
# Requirements Analysis
Typically, we place the backend return data format in a separate interface:
// Request interface data
export interface ResponseData<T = any> {
/**
* Status code
* @type { number }
*/
code: number
/**
* Data
* @type { T }
*/
result: T
/**
* Message
* @type { string }
*/
message: string
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
We can extract the API into a separate module:
import { ResponseData } from './interface.ts';
export function getUser<T>() {
return axios.get<ResponseData<T>>('/somepath')
.then(res => res.data)
.catch(err => console.error(err))
}
2
3
4
5
6
7
Then we specify the return data type User, which allows TypeScript to correctly infer the type we want:
interface User {
name: string
age: number
}
async function test() {
// user is inferred as
// {
// code: number,
// result: { name: string, age: number },
// message: string
// }
const user = await getUser<User>()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# Adding Generic Parameters to Interfaces
Based on the requirements analysis, we need to add generic parameters to the relevant interface definitions.
types/index.ts:
export interface AxiosResponse<T = any> {
data: T
status: number
statusText: string
headers: any
config: AxiosRequestConfig
request: any
}
export interface AxiosPromise<T = any> extends Promise<AxiosResponse<T>> {
}
export interface Axios {
request<T = any>(config: AxiosRequestConfig): AxiosPromise<T>
get<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
delete<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
head<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
options<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
}
export interface AxiosInstance extends Axios {
<T = any>(config: AxiosRequestConfig): AxiosPromise<T>
<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Here we first added a generic parameter T to the AxiosResponse interface, where T=any means the default value of the generic type parameter is any.
Then we added generic parameters to the AxiosPromise, Axios, and AxiosInstance interfaces. We can see that the return types of all these requests have become AxiosPromise<T>, which is Promise<AxiosResponse<T>>. This way, we can retrieve the type T from the response.
# Writing the Demo
examples/extend/app.ts:
interface ResponseData<T = any> {
code: number
result: T
message: string
}
interface User {
name: string
age: number
}
function getUser<T>() {
return axios<ResponseData<T>>('/extend/user')
.then(res => res.data)
.catch(err => console.error(err))
}
async function test() {
const user = await getUser<User>()
if (user) {
console.log(user.result.name)
}
}
test()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
When we call getUser<User>, it is equivalent to calling axios<ResponseData<User>>, meaning the type T we pass to the axios function is ResponseData<User>. This is equivalent to T in the return value AxiosPromise<T>, which is actually the T in Promise<AxiosResponse<T>> being of type ResponseData<User>. So the data type in the response data is ResponseData<User>, which has the following data structure:
{
code: number
result: User
message: string
}
2
3
4
5
This is also the data type of the return value user from const user = await getUser<User>(), so TypeScript can correctly infer the type of user.
At this point, our ts-axios interface extensions chapter is complete. In the next chapter, we will implement a very useful feature of axios -- interceptors.