如何通过Enum从Interface推导类型?TypeScript泛型报错解决
问题背景
我定义了资源类型枚举ResourceType:
enum ResourceType { Blog = "blogs", BlogAuthor = "blog_authors", [...] }
以及对应资源类型的定义接口ResourceDefinitions:
interface ResourceDefinitions { [ResourceType.Blog]: BlogResource; [ResourceType.BlogAuthor]: BlogAuthorResource; [...] }
目标是调用const resource = useResource(ResourceType.Blog, id);时,resource能自动推导为BlogResource类型。最初尝试的useResource定义如下:
const useResource = <P extends ResourceType, T = ResourceDefinitions[P]>( type: P, id: string ): T => { [...] };
但这段代码触发TypeScript错误:TS2536: Type 'P' cannot be used to index type 'ResourceDefinitions'。
目前有两种可行但不够优雅的方案:
- 手动指定泛型类型
const useResource = <T>( type: ResourceType, id: string ): T => { [...] }; const resource = useResource<BlogResource>(ResourceType.Blog, id);
- 手动添加类型断言
const useResource = <P extends keyof ResourceDefinitions, T = ResourceDefinitions[P]>( type: P, id: string ): T => { [...] }; const resource = useResource(ResourceType.Blog as keyof ResourceDefinitions, id);
优雅解决方案
只需要简化泛型定义,直接让返回类型关联枚举参数与资源定义的映射:
const useResource = <P extends ResourceType>( type: P, id: string ): ResourceDefinitions[P] => { // 内部实现逻辑 };
效果说明
调用useResource(ResourceType.Blog, id)时,TypeScript会自动将泛型P推导为ResourceType.Blog,进而返回ResourceDefinitions[ResourceType.Blog]对应的BlogResource类型,完全满足自动推导需求,且无任何类型错误。
原理
原来的错误是因为TypeScript无法默认确认P extends ResourceType的所有成员都属于ResourceDefinitions的有效键,但我们的ResourceDefinitions的键本身就是ResourceType的枚举值,因此直接将返回类型设为ResourceDefinitions[P],同时保留P extends ResourceType的约束,TypeScript就能正确识别这个映射关系,无需额外的断言或手动指定类型。
内容的提问来源于stack exchange,提问作者Arthur

