在我正在迁移到 TypeScript (TS) 的项目中,我有一个响应拦截器r => r.data
。 我如何通知 TS 我不期望AxiosResponse
类型? 我尝试过使用as Array<...
进行覆盖,但这不起作用,因为AxiosResponse
不能转换为Array
(例如,没有 .length)。
谢谢!
使用axios.request<T>(...args)
样式定义。
数组中的最后一个 Response-interceptor 隐式地遵守像(currentResponse: any) => T
这样的接口
因此,如果您的data
类似于:
interface ServerResponse {
data: ServerData
}
interface ServerData {
foo: string
bar: number
}
然后你可以说:
axios.request<ServerData>({
url: 'https://example.com/path/to/data',
transformResponse: (r: ServerResponse) => r.data
}).then((response) => {
// `response` is of type `AxiosResponse<ServerData>`
const { data } = response
// `data` is of type ServerData, correctly inferred
})
@zcei哦,实际上这对全局拦截器不起作用,例如axios.interceptors.response.use
我认为确实如此 - 拦截器目前是非常“宽松”的类型(又名any
),所以你可以附加响应拦截器并拥有它(r: any): any => r.data
(这基本上就像你会在非严格模式下省略任何输入)。
只有在您调用axios.request
时,您才能将其配置为泛型,然后将其作为data
属性的类型传递。
(实际上我在上面的代码片段中犯了一个错误, data
实际上是AxiosResponse<ServerData>
它有一个名为.data
的字段,类型ServerData
- 更新了它)
@zcei嗨,抱歉,但这确实不适用于我的示例,我创建了一个代码框来突出显示此问题。
遇到同样的问题,拦截器基本上可以归结为:
this.instance.interceptors.response.use(response => response.data);
尽管如此,返回类型
this.instance.post<string>(...);
是AxiosPromise<string>
,它期望数据被包装。
这是故意的。 您应该在拦截器中转换数据,而不是提升响应键。
我以为你会有像这样的服务器响应
{
"data": {
"foo": 42
}
}
并想摆脱response.data.data
嵌套。 如果您在拦截器中返回response.data
,那么您以后可以通过response.data.foo
而不是response.data.data.foo
访问它。
但是访问response.foo
是行不通的,因为这是跟踪其他内容(如响应代码等)的“根”响应级别。
抱歉,但我不同意您的前提,至少在功能方面不同意。
如果不使用拦截器来解包数据属性,我将不得不这样做
this.instance.post<string>(...).then(response => response.data);
this.instance.get<number>(...).then(response => response.data);
在我定义的_每个_端点中。 所有这些代码重复有什么意义?
每当请求成功时,有then
意味着,我并不真正关心响应代码等。我只关心响应代码,如果某些东西_没有_工作,这就是错误处理程序的用途,无论是在拦截器上还是在特定端点上。
this.instance.interceptors.response.use(response => {
// Everything went well, pass only relevant data through
return response.data;
}, error => {
// Something went wrong, figure out how to handle it here or in a `.catch` somewhere down the pipe
});
每当请求成功时,有 then 意味着,我并不真正关心响应代码
好吧,您可能不关心任何其他信息,但是限制每个人使用您只关心主体的HTTP 客户端并不是真正的解决方案。
即使调用成功,也有足够多的合法用例用于状态代码和标头。 就像区分204
与200
,检查速率限制标头,提取Link
标头以获取其他资源(分页)等。
如果你不在乎,把 Axios 包起来,扔掉所有东西:
this.instance.request = <T>(...args) => {
return axios.request<T>(...args).then(({ data }) => data)
}
this.instance.request<string>({ method: 'post', ... }).then(data => { ... });
this.instance.request<number>({ method: 'get', ... }).then(data => { ... });
我不明白你从哪里得到这样一种观念,即每个人都应该像我在一个项目中那样做事。 我只是想解决一个如何在每个请求上自动解包数据的问题,鉴于这个问题就在我面前,我不是唯一一个遇到这个问题的人。
拦截器_似乎_喜欢做这件事的正确地方,无论是他们的名字还是他们在自述文件中的描述(对每个响应都做一些事情)。 处理204
与200
等然后传递数据在拦截器中也是有意义的,因为这样你就可以在一个中心位置完成它。
在我看来,直观的解决方案是从拦截器返回你想要的任何东西,而不是让库以单一方式强制执行。
如果要转换数据中的某些字段:
this.instance.interceptors.response.use(response => {
response.data.foo = JSON.parse(response.data.bar);
return response;
});
如果你只想解开 Axios 的数据:
this.instance.interceptors.response.use(response => response.data);
这将使开发人员可以选择做什么,在我看来,这比一个非常固执己见的拦截器要好。 当然,你可以不同意。
你在推理“所有代码重复的重点”,所以我只是试图解释为什么这些信息是必要的。
如果任何拦截器可以在没有通用结构的情况下扰乱整个响应,我真的无法找到一种方法来保持拦截器和响应中的类型安全。 :/
我猜一个请求的响应类型需要变成any
并把精力放在开发人员手中,以确保他们做正确的事情。
对我来说,由于轻松的打字,这至少听起来比访问一个属性更容易出错。
如果您能提出一个 PR,允许拦截器覆盖根响应,同时保持“80% 用例”的类型安全,我会非常高兴!
问题是,我制作的上述两个示例都已经按照我描述的方式工作,功能方面,只是类型错误。 如果在拦截器中返回(修改后的)响应之外的任何其他内容都是错误的,我认为在那里更新预期的类型会很好。
首先,知道你在做同样的事情很有趣@Etheryte !
在我们的应用程序中, r => r.data
是链中的最终响应拦截器,我们使用其他依赖状态码来处理刷新令牌等的拦截器,但在全局级别,因为我们不需要针对特定的处理来电。
我知道可能无法使用 TS 来适应这个用例。 但是, @zcei不可否认,这是使用 Axios 的合法方式,因为它使用的是 Axios API 的官方部分(拦截器):)。
嗬! 我一直在查看错误的代码🤦♂️,并且在transformResponse
部分,而不是拦截器 - 非常抱歉!
您将如何更新类型以说明拦截器更改返回类型?
也许是这样的?
interface AxiosInstance {
request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
}
你会这样称呼它:
axios.request<User, string>({
method: 'get',
url: '/user?id=12345'
})
.then((foo: string) => { // <-- you could leave out the type annotation here, it's inferred
console.log(foo)
})
刚刚在本地尝试过,似乎可以满足您的需求。
也许我可以在晚上一起获得 PR 来更新所有方法。
@zcei看起来不错! 默认使用 AxiosResponse 当然在 99% 的情况下都是有意义的 👍
很高兴听到! 再次为我的困惑感到抱歉😓
@zcei没问题! 出于兴趣,Axios 的发布周期是什么?
没有 - 我仍然有一些关于 v1 alpha (#1333) 的未决事项,与此同时, @nickuraltsev / @emilyemorehouse会在必要时发布。
自从v0.18
以来,已经有了一些吸引力(包括我最喜欢的:实例的范围选项),所以我想他们可以取消发布。 有关更多信息,我只想邀请您到Gitter ,以便我们保持问题的重点🙂
我一直希望发布 0.19 版本,但最后我检查了一下,master 未能通过 CI。 一旦我们获得 1.0 版本,我肯定会想要一个更可靠/更常规的发布时间表。
遇到同样的问题。 有什么解决办法吗?
@qidaneix您可以尝试安装npm install axios/axios#master
直到发布。 #1605 应该已经修复了。 很高兴得到一些反馈,这是否真的有助于现实生活中的用例而不仅仅是测试🙂
@zcei我明天试试
@zcei请问什么时候发布?
+1 1.0.0 何时发布?
这肯定是 1.0 之前的版本。 @Khaledgarbaya您是否也被 Matt 添加到 NPM 中? 否则我们需要善待其他发布它的维护者😉
大家好。 我目前正在管理 NPM 版本。 我很想发布另一个 1.0.0 之前的版本,但是 Travis 测试失败了,我还没有机会修复它们。 一旦他们修好了,我很高兴立即把它弄出来😬
@zcei我没有被添加到 npm 仓库中,我只能合并更改
这个问题有什么更新吗,在不久的将来会有版本吗,比如第二个月?
+1
❤️如果现在可以发布
我们知道,但目前已被阻止。 🙁 你可以在这里获取更多信息: https ://github.com/axios/axios/issues/1657#issuecomment -410766031
作为 0.19.0-beta.1 的一部分发布。
这可以使用npm install [email protected]
或npm install axios@next
安装
也许是这样的?
interface AxiosInstance { request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>; }
你会这样称呼它:
axios.request<User, string>({ method: 'get', url: '/user?id=12345' }) .then((foo: string) => { // <-- you could leave out the type annotation here, it's inferred console.log(foo) })
刚刚在本地尝试过,似乎可以满足您的需求。
也许我可以在晚上一起获得 PR 来更新所有方法。
User(T, the first generic param) 好像没用过,如果我想使用自定义返回类型,我看起来很奇怪😕
axios.request<void, string>({
...
})
使用 void 可能更清楚。
@emilyemorehouse 听起来并不忘恩负义,但 0.19-beta 版已经开放了三个月,GA 版本是否有预计到达时间? 我们的项目有一个问题,需要这些修复之一。 我在 Gitter 频道中问过这个问题,但维护人员似乎没有在那边回应......
也许更好的措辞是在发布之前需要解决哪些问题,是否有地方可以跟踪它? 由于似乎有很多兴趣,分散工作并清楚地了解所需的工作可以帮助加快速度。
0.19 已经有一个项目里程碑,但列出的票数几个月来没有任何变化。
const func1: any = () => { return axios.request(...) }
我在axios.d.ts
中覆盖AxiosResponse
#$ :
import axios from 'axios'
declare module 'axios' {
export interface AxiosResponse<T = any> extends Promise<T> {}
}
顶撞这个问题。 它工作......好吧,当我刚刚将 AxiosInstance 定义复制到本地类型时,但在我看来,实现的解决方案非常冗长,除非我做错了什么(不是 Typescript 专家)。 由于我使用的是使用axios.create
创建的单独 axios 实例并使用此拦截器:
AxiosClient.interceptors.response.use(
response => response && response.data,
error => error && error.response && error.response.data
);
其中response.data
总是具有这种形式:
export interface APIResponse<T = any> {
data: T;
message: string;
status: boolean;
}
看来我必须像这样使用AxiosClient.post
:
AxiosClient.post<EndpointAPIResponse, APIResponse<EndpointAPIResponse>>('/endpoint', { someData });
在.then
中有正确的类型。 我在这里做错了什么还是真的应该那么冗长? 如果没有,如果我可以在创建实例时传递预期的响应模式会更好,但我不能让它工作:
export interface AxiosInstance<R> {
<T = any>(config: AxiosRequestConfig): Promise<R<T>>;
<T = any>(url: string, config?: AxiosRequestConfig): Promise<R<T>>;
defaults: AxiosRequestConfig;
interceptors: {
request: AxiosInterceptorManager<AxiosRequestConfig>;
response: AxiosInterceptorManager<R>;
};
getUri(config?: AxiosRequestConfig): string;
request<T = any>(config: AxiosRequestConfig): Promise<R<T>>;
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<R<T>>;
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<R<T>>;
head<T = any>(url: string, config?: AxiosRequestConfig): Promise<R<T>>;
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R<T>>;
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R<T>>;
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R<T>>;
}
export interface AxiosStatic extends AxiosInstance<AxiosResponse> {
create<T = AxiosResponse>(config?: AxiosRequestConfig): AxiosInstance<T>;
Cancel: CancelStatic;
CancelToken: CancelTokenStatic;
isCancel(value: any): boolean;
all<T>(values: (T | Promise<T>)[]): Promise<T[]>;
spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
}
它适用于没有泛型类型的axios.create()
或只是axios
,但如果我像这样传递我的界面:
const AxiosClient = axios.create<APIResponse>({
// ...
});
然后像这样使用它: AxiosClient.post<string>('/endpoint').then(value => value.data)
, value.data
有类型...最终以某种彻底的灾难告终。 谁能帮我解决这个问题?
编辑:好的,我想它不会起作用,因为不可能以这种方式使用泛型(所以R<T>
当 R 是泛型类型时语法不正确,但我猜 WebStorm 出于某种原因没有突出显示对我来说); https://github.com/Microsoft/TypeScript/issues/1213我认为这会解决它,但不知道它是否会被实施。
const request = <T>(options: AxiosRequestConfig) => {
return axios.request<IResponse<T>>(options).then<T>(response => {
const data = response.data;
if (data.c !== '0') {
return Promise.reject(new Error(data.m || 'error'));
}
return data.d;
});
}
采用:
request<IApiGetBrandGoodsInfoByIds>({
method: 'GET',
url: '/api/a/b',
});
@zcei这个问题解决了吗? 我无法让以下工作:
// so I created an axios instance:
const instance = axios.create(someOptions);
// then used interceptors
instance.interceptors.response.use((res) => res.data.data); // server response will always have 'data'
// then when using the following to make a request
instance.get<string>(someURI); // suppose server response was {data: 'some message'}
// ^^ the above returns type AxiosPromise<string>. How do I get it to return type Promise<string> instead?
有同样的问题,我安装的是0.19beta版,ts也无法解析正确的类型
我在
axios.d.ts
中覆盖AxiosResponse
#$ :import axios from 'axios' declare module 'axios' { export interface AxiosResponse<T = any> extends Promise<T> {} }
哦牛皮
我使用拦截器执行以下操作: response => response.data
。 所以我需要完全删除AxiosResponse
包装器。
我最终得到:
import axios from 'axios'
declare module 'axios' {
export interface AxiosInstance {
request<T = any> (config: AxiosRequestConfig): Promise<T>;
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
head<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
}
}
如果有人不知道如何使用它:
{
"compilerOptions": {
"typeRoots": [
"./node_modules/@types",
"./src/types/",
]
}
}
这对我有用:
Api.boards.createBoard = jest.fn((name:string) => {
const mockBoardResult = {
created_on: mockBoardData.date,
name,
threads: [],
updated_on: mockBoardData.date,
_id: mockBoardData._id
};
return Promise.resolve({data:mockBoardResult}) as Promise<AxiosResponse<any>>
});
另一种覆盖的方法
import * as axios from 'axios'
declare module 'axios' {
interface AxiosInstance {
(config: AxiosRequestConfig): Promise<any>
}
}
我认为我们可能不应该在使用拦截器时向AxiosResponse
添加属性,因为拦截器可以被弹出。
// @axios-exts/add-foo-to-resp
declare module 'axios' {
interface AxiosResponse<T = any> {
foo: string
}
}
const addFooToResp = (res: AxiosResponse) => {
res.foo = 'bar'
return res
}
export default addFooToResp
// Your Project
import axios from 'axios'
import addFooToResp from '@axios-exts/add-foo-to-resp'
var id = axios.interceptors.response.use(addFooToResp)
axios.get('xx').then(res => {
// have type defined
// because we use `declare module 'axios'` ts can infer type
console.log(res.foo)
})
// but if then eject?
axios.interceptors.response.eject(id)
axios.get('xx').then(res => {
// also have type defined, it's maybe not reasonable
console.log(res.foo)
})
为什么我们使用打字稿? 因为我们希望我们的项目是安全的。
如果有一天我们从基本 utils 中删除一个属性,我们希望引用它的代码在编译时产生错误。
所以,我们想用拦截器给AxiosResponse
添加一些属性并进行类型推断,这是矛盾的,因为没有办法确保在拦截器被弹出时可以更新类型推断
我认为 axios 应该告诉用户:
拦截器应该处理对AxiosResponse
没有影响的东西,如果你想扩展AxiosResponse
属性并进行类型推断,你应该喜欢'plugin'
// @axios-exts/add-foo-to-resp
declare module 'axios' {
interface AxiosResponse<T = any> {
foo: string
}
}
const addFooToResp = (res: AxiosResponse) => {
res.foo = 'bar'
return res
}
export default (axios) => {
axios.interceptors.response.use(addFooToResp)
}
// Your Project
import axios from 'axios'
import addFooToResp from '@axios-exts/add-foo-to-resp'
addFooToResp(axios)
这不是 100% 安全,但它比仅使用axios.interceptors.response.use
更安全
我推荐 axios 设计一个插件系统,现在我们总是看到
import axios from 'axios'
import wrapper from 'axios-cache-plugin'
let http = wrapper(axios, {
maxCacheSize: 15
})
export default http
使用like wrapper
连接插件到axios,每个插件没有通用规则,不够优雅。 我想应该喜欢
import axios from 'axios'
import axiosCache from 'axios-cache-plugin'
axios.use(axiosCache, {
// some options
})
export axios
// axios-cache-plugin
export default (axios) => {}
// or
export default {
install(axios){}
}
// like Vue.use for Vue.js
使用
axios.request<T>(...args)
样式定义。
数组中的最后一个 Response-interceptor 隐式地遵守像(currentResponse: any) => T
这样的接口因此,如果您的
data
类似于:interface ServerResponse { data: ServerData } interface ServerData { foo: string bar: number }
然后你可以说:
axios.request<ServerData>({ url: 'https://example.com/path/to/data', transformResponse: (r: ServerResponse) => r.data }).then((response) => { // `response` is of type `AxiosResponse<ServerData>` const { data } = response // `data` is of type ServerData, correctly inferred })
请告诉我如何使用 tsx 在 Axios 请求中添加标头和配置
@MoZiadAlhafez
declare module 'axios' {
interface AxiosRequestConfig {
useCache: boolean
}
}
但这不推荐
这是一个很长的故事,但似乎已经在#1605 中解决了。 请参阅https://github.com/axios/axios/issues/1510#issuecomment -396894600。
如果我误解了某些东西,最好打开一个新问题并参考此处。 谢谢。
最有用的评论
我在
axios.d.ts
中覆盖AxiosResponse
#$ :