如何移除postEntity函数中的void返回类型并替换为never?
我明白你的问题啦——你希望在循环调用支持post的实体时,postEntity的返回类型里不要混入void,要么把它替换成never,要么直接让不支持post的实体无法传入这个函数。下面给你两种可行的解决思路:
方案一:直接定义支持post的实体子集类型
这个方法最简洁,核心是让类型系统明确知道哪些实体真正支持post,从根源上排除不支持的实体(比如Job)。
首先,把支持post的实体数组改成只读常量,这样TypeScript能精准推导它的类型:
const entityEndpointsSupportingPost = [Entity.Person, Entity.Organization] as const;
然后调整EntityEndpointsSupportingPost,只保留支持post的实体对应的返回类型:
interface Person {} interface Organization {} enum Entity { Person, Organization, Job, } type EntityEndpointsSupportingPost = { [Entity.Person]: Person; [Entity.Organization]: Organization; };
接着从数组推导支持post的实体类型:
type SupportedPostEntity = typeof entityEndpointsSupportingPost[number];
最后修改postEntity的泛型约束,只接受支持post的实体:
async function postEntity<T extends SupportedPostEntity>(endpoint: T): Promise<EntityEndpointsSupportingPost[T]> { const result = await fetch(`api/${endpoint}`); return await result.json(); }
这样一来:
- 单独调用
postEntity(Entity.Person)时,返回类型还是Promise<Person>,完全没问题; - 循环
entityEndpointsSupportingPost时,entity的类型会被推断为Entity.Person | Entity.Organization,postEntity(entity)的返回类型就是Promise<Person | Organization>,再也不会出现void了。
方案二:保留原有类型结构,用条件类型替换void为never
如果你需要保留EntityEndpointsSupportingPost的完整结构(比如要明确标记Job不支持post),可以用条件类型把void替换成never,同时限制函数只接受返回类型不是never的实体。
首先定义一个工具类型,把void替换为never:
type NonVoidPostReturn<T extends keyof EntityEndpointsSupportingPost> = EntityEndpointsSupportingPost[T] extends void ? never : EntityEndpointsSupportingPost[T];
然后筛选出支持post的实体类型(排除返回类型为never的实体):
type SupportedPostEntity = Exclude<keyof EntityEndpointsSupportingPost, { [K in keyof EntityEndpointsSupportingPost]: NonVoidPostReturn<K> extends never ? K : never }[keyof EntityEndpointsSupportingPost]>;
最后修改postEntity函数:
async function postEntity<T extends SupportedPostEntity>(endpoint: T): Promise<NonVoidPostReturn<T>> { const result = await fetch(`api/${endpoint}`); // 这里需要类型断言,因为JSON.parse返回的是unknown,TypeScript无法自动推导到具体类型 return await result.json() as NonVoidPostReturn<T>; }
这个方案下:
- 如果你尝试传入
Entity.Job,TypeScript会直接报错,因为它不在SupportedPostEntity范围内; - 循环支持post的实体时,返回类型同样不会包含
void,完美符合你的需求。
为什么原来的代码会出现void?
简单来说,你原来的postEntity泛型约束是keyof EntityEndpointsSupportingPost,这个类型包含了Entity.Job,所以返回类型自然会带上void。而循环的数组entityEndpointsSupportingPost默认被推断为Entity[],不是精准的子集类型,所以TypeScript会认为你可能传入任何实体,导致返回类型出现void | Person | Organization。上面两个方案都是通过缩小类型范围来解决这个问题的。
备注:内容来源于stack exchange,提问作者tobias47n9e

