React+TypeScript中如何正确访问async函数返回的异步数据
问题根因
两个核心问题导致拿不到正确数据、触发类型报错:
- 所有
async标记的函数返回值永远是Promise对象,调用时如果不通过await等待Promise状态变更,拿到的永远是pending状态的Promise实例,不可能直接拿到内部resolve的实际数据 - 类型定义和接口实际返回结构不匹配:接口实际返回的是
WeatherAPI结构的数组,但给请求方法、axios泛型定义的返回类型是单个WeatherAPI对象,TS按照定义的类型做校验时,自然会认为单个对象上不存在数组下标0属性,抛出类型错误
修复步骤
- 第一步:对齐类型定义,把所有涉及到接口返回值的泛型、返回值类型,从单个对象改为数组类型
- 第二步:调用
getLatLong方法时加上await关键字,等待异步请求返回后再拿到实际数据做后续操作
修复后完整代码
// 单条位置数据的接口定义保持不变 export interface WeatherAPI { country: string, lat: number, local_names: object, lon: number, name: string, state: string } async getLatLong(location: string): Promise<WeatherAPI[]> { // axios.get的泛型要和response.data的实际结构对齐,这里传数组类型 const response = await axios.get<WeatherAPI[]>(`${this.latLongUrl}q=${location}`) return response.data; } async getWeather(location: string) { // 加await等待异步请求完成,拿到实际的数组数据 const latLong = await this.getLatLong(location); // 此时latLong的类型被正确推导为WeatherAPI[],下标访问不会触发类型报错 console.log(latLong[0]); }
React场景额外注意
如果是在函数组件的useEffect中触发上述请求,不能直接给useEffect的回调函数加async标记,需要在回调内部定义异步函数再执行,示例代码:
useEffect(() => { const loadWeatherData = async () => { const latLongList = await getLatLong('北京'); // 拿到数据后执行state更新等操作 }; loadWeatherData(); }, []);
规则总结
- 所有async函数的返回值都会被JS自动包装为Promise,必须通过
await或者.then()链式调用才能拿到内部返回的实际数据 - 给axios写泛型、给异步方法写返回值类型时,必须1:1对齐接口实际返回的结构:返回数组就写
接口名[],返回带分页、外层包裹字段的结构就补充定义对应的外层类型,不要直接用单条数据的类型代替整个返回结构 - 只要TS报类型属性不存在的错误,先检查定义的类型和实际运行时的数据结构是不是一致,不要用any强行绕过,避免后续出现更多隐性bug
内容的提问来源于stack exchange,提问作者Arak Tai'Roth
相关产品推荐
相关产品推荐

