当每个promise具有不同的返回类型时,如何键入Axios.all

时间:2018-09-19 08:09:53

标签: typescript axios typescript-typings

我有两个Axios get请求,其返回类型已明确声明。这是一个简化的示例:

interface O1 {
   prop1: string;
   prop2: boolean;
}

interface O2 {
   prop3: number;
   prop4: string;
}

interface Request<T> {
   message: string;
   data: T
}

使用这些接口,我键入了请求,并且还键入了结果对象:

const getO1 = () => Axios.get<Request<O1>>("/API/O1");
const getO2 = () => Axios.get<Request<O2>>("/API/O2");

现在我需要并行进行此调用,因此我尝试了Axiox.all,但是TypeScript抱怨O1O2类型不匹配。具体来说,我已经尝试过:

const getAll = () => Axios.all([getO1(), getO2()]);

据我所见,TypeScript期望在all的情况下,所有结果都具有相同的类型(结构)。有没有一种类型的并发调用的方法? (使用Axios v0.18和TypeScript v3.0.3)

2 个答案:

答案 0 :(得分:2)

很遗憾,Axios.all的声明不是很好。输入的方式无法传递元组。

export interface AxiosStatic extends AxiosInstance {
  all<T>(values: (T | Promise<T>)[]): Promise<T[]>;
}

该参数必须是单一类型T的数组。 Typescript不会推断T的并集,也不能推断期望数组的元组。

我们可以看看Promise.all的键入方式(它确实支持元组)

all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
// ...
all<T1, T2, T3, T4>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>]): Promise<[T1, T2, T3, T4]>;
all<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
all<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;

我们可以看到它们使用很多重载来支持元组。

如果Axios承诺符合规范,则可以尝试使用Promise.all。这些类型可以解决问题,但我自己尚未测试运行时行为。

const getAll = () => Promise.all([getO1(), getO2()]) //() => Promise<[AxiosResponse<Request<O1>>, AxiosResponse<Request<O2>>]>

或者您可以坚持使用axios版本并使用一些类型断言:

const getAll = () => Axios.all<{}>([getO1(), getO2()]) as unknown as AxiosPromise<[Request<O1>, Request<O2>]>

一个有趣的提示,在3.1中,我们将能够输入all而不会出现10个重载:

type PromiseResult<T> = T extends AxiosPromise<infer R>? R: T
type ArrayPromiseResult<T extends (any | AxiosPromise<any>)[]> = { [P in keyof T]: PromiseResult<T[P]> }
declare function all<T extends [] | (any | AxiosPromise<any>)[]>(values: T): AxiosPromise<ArrayPromiseResult<T>>

let result = all([getO1(),getO2()]) // result is AxiosPromise<[Request<O1>, Request<O2>]>

答案 1 :(得分:0)

You can create an base interface and let your O1 and O2 interfaceses extending this interface. Use the base interface as the generic type paramter inside the Axios.all statement.

  import Axios from 'axios';

    //base interface
    interface axiosResponse{}

    //extend the base interface in both interfaces
    interface O1 extends axiosResponse {
        prop1: string;
        prop2: boolean;
    }

    interface O2 extends axiosResponse {
        prop3: number;
        prop4: string;
    }

    interface Request<T> {
        message: string;
        data: T
    }

    const getO1 = () => Axios.get<Request<O1>>("/API/O1");
    const getO2 = () => Axios.get<Request<O2>>("/API/O2");

    //specify the base interface type as the generic type parameter
    const getAll = () => Axios.all<axiosResponse>([getO1(), getO2()]).then( r => {
      const result: O1 = <O1>r[0];
      const result2: O2 = <O2>r[1];
    })