如何让TypeScript推断实现泛型接口的对象的特定类型?

如何让TypeScript推断实现泛型接口的对象的特定类型?,typescript,Typescript,在这个代码示例中,TypeScript似乎不知道最后一行中userService.getByUserId的确切类型,它应该是(userId:string)=>ServiceResult。相反,它坚持使用在服务接口中定义的更通用的类型(…args:Array)=>ServiceResult 如何确保实现服务的任何对象都遵守接口定义和TypeScript知道任何实现的服务的实际类型定义,例如本例中的userService.getByUserId 谢谢 type servicesult=T | Pro

在这个代码示例中,TypeScript似乎不知道最后一行中
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)