如何实现TypeScript泛型函数的返回类型中data与error始终为可选类型?
要实现你的需求——让internalHttp函数返回对象中的data和error始终为可选属性,同时兼顾类型安全,我们可以通过调整返回类型定义来达成目标。下面是两种实用的解决方案:
方案1:直接定义单一可选属性类型
这种方案最贴合你期望的{data?: Data, error?: Error}结构,直接把返回类型改成包含可选data、error和必选isError的单一类型:
// 替换原有的HttpReturnSuccess、HttpReturnError和HttpReturn定义 export type HttpReturn<TData = unknown, TError = unknown> = { data?: TData; error?: TError; isError: boolean; };
函数实现不需要修改,因为你当前的返回逻辑已经完全符合这个类型:
export const internalHttp = async <Data = unknown, Payload = unknown, Error = unknown>( props: HttpProps<Payload> ): Promise<HttpReturn<Data, Error>> => { // 原函数实现保持不变 };
现在当你解构返回值时,data和error会被自动推断为Data | undefined和Error | undefined,可以直接使用可选链调用:
const { data } = await internalHttp<{name: "amed"}>(); console.log(data?.name); // TypeScript不会报错,完美支持可选链
方案2:保留联合类型的类型安全,兼容可选访问
如果你想保留原有联合类型的严格类型约束(即isError为false时data一定存在,isError为true时error一定存在),同时支持直接用可选链访问,其实原有的联合类型已经满足需求——你只需要换一种访问方式即可。
原有的联合类型定义本身就会让data的类型推断为Data | undefined,error为Error | undefined,只是TypeScript会要求你在直接访问data.name前检查isError;但如果你使用可选链data?.name,TypeScript会直接允许这种写法,不需要额外的类型判断。
如果你想让类型定义更直观,可以用Partial工具类型包装联合类型:
// 保留原有的HttpReturnSuccess和HttpReturnError export interface HttpReturnSuccess<TData = unknown> { data: TData; error: undefined; isError: false; } export interface HttpReturnError<TError = unknown> { data: undefined; error: TError; isError: true; } // 用Partial包装联合类型,让属性变为可选 export type HttpReturn<TData = unknown, TError = unknown> = Partial<HttpReturnSuccess<TData> & HttpReturnError<TError>> & { isError: boolean; };
为什么原有代码的类型提示不符合预期?
原有的HttpReturn是联合类型,TypeScript会触发判别式类型缩小:当你检查isError为false时,它会确定data一定存在;当isError为true时,error一定存在。但如果你不检查isError直接访问data.name,TypeScript会报错——这是它的类型安全机制在起作用。而你期望的可选链访问,其实和原有类型完全兼容,只需要把data.name改成data?.name即可。
如果你希望完全去掉这种判别式约束,直接让data和error成为可选属性,方案1是最直接的选择。
内容的提问来源于stack exchange,提问作者Adiat Hasan

