根据提供的参数返回字典键的打字稿类型推断

时间:2019-01-08 17:00:45

标签: typescript

我有以下文件:

types.ts

import * as Promise from 'bluebird';

export interface ApiSpec {
    [apiReq: string]: (...args: any[]) => {
        url: string;
        contentType?: string;
        query?: {
            [key: string]: any;
        };
        method?: string;
    };
}

export type ApiRequest = {
    [T in keyof ApiSpec]: (...args: any[]) => Promise<any>;
};

Api.ts

import {ApiSpec, ApiRequest} from './types';
import * as _ from 'lodash';

export default function createApi(apiSpec: ApiSpec, concurrent: boolean = false): ApiRequest {
    return _.mapValues(apiSpec, (fn, name) => {
        return function(...args) {
            const requestSpec = fn.apply(null, args);
            requestSpec.which = name;
            requestSpec.url = `http://localhost:8989/api${requestSpec.url}`;
            // request is a function doing the API request and returning a bluebird promise
            return request(requestSpec, concurrent);
        }
    });
}

main.ts

import createApi from './Api';

// usually defined in a separate file
const ApiSpec = {
    fetchData: (start: Date, end: Date) => {
        return {
            url: `/fetchData?start=${start}&end=${end}`
        };
    }
};
const Api = createApi(ApiSpec);

Api.fetchData(new Date(), new Date()).then(res => {
    // do something with result
})

在打字稿中是否可以为我的Api变量完成打字?我希望能够知道我的API中可用的功能,最重要的是,每个API调用的参数都无需查看定义API规范的文件。但是,仅在类型中使用[T in keyof ApiSpec]不能推断出我要传递给createApi的API规范的确切键。这在打字稿中有可能吗?

注意:我正在使用2.3.4版本,目前无法更新到最新的打字稿版本

1 个答案:

答案 0 :(得分:3)

您需要使用通用类型参数来捕获传递给createApi的实际类型,并将其映射到ApiRequest中,而不是通用ApiSpec中。 ApiSpec可以用作类型参数的类型约束。

export interface ApiSpec {
    [apiReq: string]: (...args: any[]) => {
        url: string;
        contentType?: string;
        query?: {
            [key: string]: any;
        };
        method?: string;
    };
}

export type ApiRequest<T extends ApiSpec> = {
    [P in keyof T]: (...args: Parameters<T[P]>) => Promise<any>;
};



export default function createApi<T extends ApiSpec>(apiSpec: T, concurrent: boolean = false): ApiRequest<T> {
    return null!; // implementation 
}
// usually defined in a separate file
const ApiSpec = {
    fetchData: (start: Date, end: Date) => {
        return {
            url: `/fetchData?start=${start}&end=${end}`
        };
    }
};
const Api = createApi(ApiSpec);

Api.fetchData(new Date(), new Date()).then(res => {
    // do something with result
})

注意,我还添加了Parameters<T[P]>以便在结果对象中保留参数类型。

修改 对于您正在使用的Typescript版本(2.3),我们在rest参数中没有条件类型或元组,因此不幸的是,无法以类型安全的方式映射函数参数。我们能做的最好的是:

export type ApiRequest<T extends ApiSpec> = {
    [P in keyof T]: (...args: any[]) => Promise<any>;
};
相关问题