为什么typescript显示我的更具体类型的错误
我正在为我正在创建/使用的api创建一个通用api处理程序 我已经为端点引用的所有可能方式定义了类型,现在正在为每个端点创建对象 我创建了一个基于代理的通用解决方案,用于实际调用api(下面只显示了其中的一部分) 这只是一个例子,因为它被划分在不同的名称空间和文件中 我遇到的问题是,当我开始为我的为什么typescript显示我的更具体类型的错误,typescript,types,Typescript,Types,我正在为我正在创建/使用的api创建一个通用api处理程序 我已经为端点引用的所有可能方式定义了类型,现在正在为每个端点创建对象 我创建了一个基于代理的通用解决方案,用于实际调用api(下面只显示了其中的一部分) 这只是一个例子,因为它被划分在不同的名称空间和文件中 我遇到的问题是,当我开始为我的ApiService分配可能的路由时,我得到一个类型错误 Type 'IRoute' is not assignable to type 'IApiService'. Property 'TestIte
ApiService
分配可能的路由时,我得到一个类型错误
Type 'IRoute' is not assignable to type 'IApiService'.
Property 'TestItem' is missing in type 'IRoute'.
我不明白为什么TestItem
在IRoute
上“丢失”了,因为我有一个通用键,TestItem
与通用[string]:IRoute
索引匹配
也许我错过了什么。我可以返回makeApiProxy(“/api”)作为任何一个函数代码>以清除错误,但我尽量不这样做,我更希望了解我所误解的机制
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));
type with-paramsquerybody=(params:params,query:query,body:body)=>Promise;
输入WithBody=(body:body)=>body;
键入WithParams=(params:params)=>Promise;
使用paramsbody=(params:params,body:body)=>Promise输入;
键入WithParamsQuery=(params:params,query:query)=>Promise;
输入WithQuery=(查询:query)=>Promise;
输入WithNone=()=>Promise;
键入UnknownApiCall=(…参数:any[])=>any;
接口路由{
删除?:WithParams | WithParamsQuery | WithQuery;
获取?:WithParams | WithParamsQuery | WithQuery | WithNone;
补丁?:带主体|带参数主体|带参数主体;
Post?:带主体|带参数主体|带参数主体;
[键:字符串]:IRoute |函数;
}
接口ITestItemRoute扩展IRoute{
Get:WithQuery&WithParams;
职位:WithBody;
分项:{
Get:WithParams;
职位:WithBody;
};
}
接口IApiService{
测试项目:ITestItemRoute;
}
函数ApiService($http:any):IApiService{
函数ApiGet(路径:字符串){
return(…args:any[])=>{
返回$http.get(路径,参数);
};
}
函数ApiPost(路径:字符串){
return(…args:any[])=>{
返回$http.get(路径,参数);
};
}
函数makeApiProxy(路径:字符串):IRoute{
返回新的代理({}{
get:(目标,名称)=>{
交换机(名称){
案例“Get”:
返回ApiGet(路径);
案例“职位”:
返回ApiPost(路径);
违约:
返回makeApiProxy(`${path}/${name}`);
}
},
});
}
/**
*类型“IRoute”不可分配给类型“IApiService”。
*类型“IRoute”中缺少属性“TestItem”。
*
*但是为什么呢?IRoute上有通用的[key:string]索引!
*/
返回makeApiProxy(“/api”);
}
//我们将传入我们的$http服务
const api=ApiService({}如有);
api.TestItem.Get([12]),然后((TestItem:any)=>console.log(TestItem));
将这一点归结到您所问的具体问题是很有启发性的:
让我们看看两个接口:I
有一个number
属性的索引签名,O
是一个更“普通”的对象,有两个number
属性,键名为“foo”
和“bar”
注意,我们未能将类型为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'.
投诉是类型I
中缺少“foo”
。索引签名有帮助吗?没有。因此:
const goodIbadO = { baz: 4 };
i = goodIbadO; // okay
o = goodIbadO; // error
你看,goodIbadO
是一个完全有效的I
,因为“baz”
是一些字符串键,属性是一个数字
值。但它绝对不是可接受的O
,因为它缺少必要的“foo”
和“bar
”键
而且,由于并非每个I
都是O
,因此通常不能将I
值分配给O
变量
出于同样的原因,IRoute
不可分配给IApiService
。希望有帮助;祝你好运 这是有道理的。非常感谢。总之,您可以将特定的内容指定给更通用的内容,但不能将通用的内容指定给更具体的内容。
const goodIbadO = { baz: 4 };
i = goodIbadO; // okay
o = goodIbadO; // error