如何让TypeScript推断实现泛型接口的对象的特定类型?
在这个代码示例中,TypeScript似乎不知道最后一行中如何让TypeScript推断实现泛型接口的对象的特定类型?,typescript,Typescript,在这个代码示例中,TypeScript似乎不知道最后一行中userService.getByUserId的确切类型,它应该是(userId:string)=>ServiceResult。相反,它坚持使用在服务接口中定义的更通用的类型(…args:Array)=>ServiceResult 如何确保实现服务的任何对象都遵守接口定义和TypeScript知道任何实现的服务的实际类型定义,例如本例中的userService.getByUserId 谢谢 type servicesult=T | Pro
userService.getByUserId
的确切类型,它应该是(userId:string)=>ServiceResult
。相反,它坚持使用在服务
接口中定义的更通用的类型(…args:Array)=>ServiceResult
如何确保实现服务
的任何对象都遵守接口定义和TypeScript知道任何实现的服务
的实际类型定义,例如本例中的userService.getByUserId
谢谢
type servicesult=T | Promise |未定义;
/**
*在实现“服务”时,必须有一个采用
*单参数'id',类型为'string',返回'servicesult'。
*
*实现“Service”的对象上的任何其他属性必须是函数
*它接受任意数量的参数并返回“ServiceResult”。
*/
接口服务{
get:(id:string)=>ServiceResult;
[键:字符串]:(…参数:数组)=>ServiceResult;
}
类型用户={
id:字符串;
名称?:字符串;
};
常量用户:数组=[
{
id:'a',
姓名:“乔恩”,
},
];
const userService:Service={
get:(id:string)=>users.find((user)=>user.id==id),
/**
*在这里,`getByUserId`有一个定义良好的
*`(userId:string)=>ServiceResult`
*/
getByUserId:(userId:string)=>users.find((user)=>user.id==userId),
};
/**
*但是,当调用“getByUserId”时,TypeScript似乎不知道
*它的确切类型定义,而是坚持
*`(…args:any[])=>ServiceResult`
*/
getByUserId();
由于userService
不知道getByUserId
的实际类型,因此必须显式重载它
const userService: Service<User> & {getByUserId: (userId: string) => ServiceResult<User>} = {
get: (id: string) => users.find((user) => user.id === id),
getByUserId: (userId: string) => users.find((user) => user.id === userId),
};
userService.getByUserId(); // error now
constuserservice:Service&{getByUserId:(userId:string)=>servicesult}={
get:(id:string)=>users.find((user)=>user.id==id),
getByUserId:(userId:string)=>users.find((user)=>user.id==userId),
};
userService.getByUserId();//现在出错
由于userService
不知道getByUserId
的实际类型,因此必须显式重载它
const userService: Service<User> & {getByUserId: (userId: string) => ServiceResult<User>} = {
get: (id: string) => users.find((user) => user.id === id),
getByUserId: (userId: string) => users.find((user) => user.id === userId),
};
userService.getByUserId(); // error now
constuserservice:Service&{getByUserId:(userId:string)=>servicesult}={
get:(id:string)=>users.find((user)=>user.id==id),
getByUserId:(userId:string)=>users.find((user)=>user.id==userId),
};
userService.getByUserId();//现在出错
当使用(非并集)类型注释变量声明时,编译器将忘记指定给它的任何较窄表达式的类型。没有内置功能可以说“请推断变量的类型,但确保它可分配给此给定类型”。您要么停止注释,让编译器推断变量的类型,要么进行注释,得到您注释的确切类型
GitHub存在一个问题,要求提供这样的功能,但我认为不会有太多的事情发生
一种方法是不注释变量,如果初始值设定项不正确,当您稍后尝试在某个地方使用它时,您将得到一个错误。例如:
const userServiceNoAnnotation = {
get: (id: string) => users.find((user) => user.id === id),
getByUserId: (userId: string) => users.find((user) => user.id === userId),
};
const badUserServiceNoAnnotation = {
get: (id: number) => users.find((user) => user.id === id.toString()),
getByUserId: (userId: string) => users.find((user) => user.id === userId),
};
这里我省略了注释,编译器推断出每个变量的类型足够窄,不会忘记getByUserId()
的参数。badUserServiceNoAnnotation
中没有错误。但当我们实际尝试将它们用作服务时,我们会得到您想要的行为:
function expectUserService(us: Service<User>) { }
expectUserService(userServiceNoAnnotation);
expectUserService(badUserServiceNoAnnotation); // error!
// -------------> ~~~~~~~~~~~~~~~~~~~~~~~~~~
// Types of property 'get' are incompatible.
而好的则能满足你的需求:
const userService = checkUserService({
get: (id: string) => users.find((user) => user.id === id),
getByUserId: (userId: string) => users.find((user) => user.id === userId),
});
userService.getByUserId(); // error!
// -------> ~~~~~~~~~~~~~
// Expected 1 arguments, but got 0.
当使用(非并集)类型注释变量声明时,编译器将忘记指定给它的任何较窄表达式的类型。没有内置功能可以说“请推断变量的类型,但确保它可分配给此给定类型”。您要么停止注释,让编译器推断变量的类型,要么进行注释,得到您注释的确切类型
GitHub存在一个问题,要求提供这样的功能,但我认为不会有太多的事情发生
一种方法是不注释变量,如果初始值设定项不正确,当您稍后尝试在某个地方使用它时,您将得到一个错误。例如:
const userServiceNoAnnotation = {
get: (id: string) => users.find((user) => user.id === id),
getByUserId: (userId: string) => users.find((user) => user.id === userId),
};
const badUserServiceNoAnnotation = {
get: (id: number) => users.find((user) => user.id === id.toString()),
getByUserId: (userId: string) => users.find((user) => user.id === userId),
};
这里我省略了注释,编译器推断出每个变量的类型足够窄,不会忘记getByUserId()
的参数。badUserServiceNoAnnotation
中没有错误。但当我们实际尝试将它们用作服务时,我们会得到您想要的行为:
function expectUserService(us: Service<User>) { }
expectUserService(userServiceNoAnnotation);
expectUserService(badUserServiceNoAnnotation); // error!
// -------------> ~~~~~~~~~~~~~~~~~~~~~~~~~~
// Types of property 'get' are incompatible.
而好的则能满足你的需求:
const userService = checkUserService({
get: (id: string) => users.find((user) => user.id === id),
getByUserId: (userId: string) => users.find((user) => user.id === userId),
});
userService.getByUserId(); // error!
// -------> ~~~~~~~~~~~~~
// Expected 1 arguments, but got 0.
您只需要将您的UserService
声明为实现接口的类。
这样,您就可以将某些方法缩小到正确的类型,这样您就可以在编译时告诉函数需要哪些参数
type servicesult=T | Promise |未定义;
接口服务{
get:(id:string)=>ServiceResult;
[键:字符串]:(…参数:数组)=>ServiceResult;
}
界面用户{
id:字符串;
名称:字符串;
}
常量用户:用户[]=[
{
id:“a”,
姓名:“乔恩”
}
];
类UserService实现服务{
//这是必需的,因为如果我们不添加这一行,Typescript会抱怨
//此类的每个属性都必须是此类函数
[键:字符串]:(…参数:数组)=>ServiceResult;
获取(id:string){
返回users.find(u=>u.id==id);
}
getByName(名称:字符串){
返回users.find(u=>u.name==name);
}
异步getByIdAsync(id:string)