首页 > 解决方案 > 基于提供的参数的返回字典键的打字稿类型推断

问题描述

我有以下文件:

类型.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>;
};

阿皮兹

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]在类型中使用并不能推断出我传递给的 API 规范的确切键createApi。这在打字稿中可能吗?

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

标签: typescript

解决方案


您需要使用泛型类型参数来捕获传入的实际类型createApi并将其映射ApiRequest而不是泛型ApiSpecApiSpec可以用作类型参数的类型约束。

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]>以在结果对象中保留参数类型。

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

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

推荐阅读