我正在为我正在创建/使用的API创建通用api处理程序。
我为端点可以引用的所有可能方式定义了类型,现在正在为每个端点创建对象。
我创建了一个基于代理的通用解决方案,用于实际调用api(其中只有一部分如下所示)。
这只是例如,因为它分为不同的命名空间和文件。
我遇到的问题是当我开始将可能的路线分配给ApiService
时,我收到类型错误。
Type 'IRoute' is not assignable to type 'IApiService'.
Property 'TestItem' is missing in type 'IRoute'.
我不明白为什么TestItem
是"缺少"与IRoute
上一样,我有一个通用密钥,TestItem
与通用[string]: IRoute
索引相匹配。
也许我错过了什么。我可以return return makeApiProxy("/api") as any;
清除错误,但我尽量不这样做,我宁愿理解我误解的机制。
type WithParamsQueryBody<Params extends any[], Query, Body, Response> = (params: Params, query: Query, body: Body) => Promise<Response>;
type WithBody<Body, Response> = (body: Body) => Body;
type WithParams<Params extends any[], Response> = (params: Params) => Promise<Response>;
type WithParamsBody<Params extends any[], Body, Response> = (params: Params, body: Body) => Promise<Response>;
type WithParamsQuery<Params extends any[], Query, Response> = (params: Params, query: Query) => Promise<Response>;
type WithQuery<Query, Response> = (query: Query) => Promise<Response>;
type WithNone<Response> = () => Promise<Response>;
type UnknownApiCall = (...params: any[]) => any;
interface IRoute {
Delete?: WithParams<any[], any> | WithParamsQuery<any[], any, any> | WithQuery<any, any>;
Get?: WithParams<any[], any> | WithParamsQuery<any[], any, any> | WithQuery<any, any> | WithNone<any>;
Patch?: WithBody<any, any> | WithParamsBody<any[], any, any> | WithParamsQueryBody<any[], any, any, any>;
Post?: WithBody<any, any> | WithParamsBody<any[], any, any> | WithParamsQueryBody<any[], any, any, any>;
[key: string]: IRoute | Function;
}
interface ITestItemRoute extends IRoute {
Get: WithQuery<any, any[]> & WithParams<[number], any>;
Post: WithBody<any, any>;
SubItem: {
Get: WithParams<[number], any[]>;
Post: WithBody<any, any>;
};
}
interface IApiService {
TestItem: ITestItemRoute;
}
function ApiService($http: any): IApiService {
function ApiGet(path: string) {
return (...args: any[]) => {
return $http.get(path, args);
};
}
function ApiPost(path: string) {
return (...args: any[]) => {
return $http.get(path, args);
};
}
function makeApiProxy(path: string): IRoute {
return new Proxy({}, {
get: (target, name) => {
switch (name) {
case "Get":
return ApiGet(path);
case "Post":
return ApiPost(path);
default:
return makeApiProxy(`${path}/${name}`);
}
},
});
}
/**
* Type 'IRoute' is not assignable to type 'IApiService'.
* Property 'TestItem' is missing in type 'IRoute'.
*
* But why? IRoute has the generic [key: string] index on it!
*/
return makeApiProxy("/api");
}
// We would pass in our $http service
const api = ApiService({} as any);
api.TestItem.Get([12]).then((testItem: any) => console.log(testItem));
答案 0 :(得分:1)
将此问题解决为您所询问的具体问题是有益的:
让我们看看两个接口:I
具有number
属性的索引签名,O
是一个更“普通”的对象,具有两个number
属性,其中的键名为"foo"
和"bar"
。
interface I {
[key: string]: number;
}
interface O {
foo: number;
bar: number;
}
注意我们未能将I
类型的值分配给O
类型的变量:
declare let i: I;
let o: O = i; // error:
// Type 'I' is not assignable to type 'O'.
// Property 'foo' is missing in type 'I'.
投诉是"foo"
类型中缺少I
。索引签名不帮助吗?不。因此:
const goodIbadO = { baz: 4 };
i = goodIbadO; // okay
o = goodIbadO; // error
您看,goodIbadO
是完全有效的I
,因为"baz"
是一些字符串键,属性是number
值。但它绝对不是可接受的O
,因为它缺少必要的"foo"
和"bar
“`键。
由于并非每个I
都是O
,因此您无法将I
值分配给O
变量。
并且,出于同样的原因,IRoute
无法分配给IApiService
。希望有所帮助;祝你好运!