TypeScript中Promise包裹布尔字面量联合类型的XHR响应类型问题
这个错误的核心是TypeScript自动把你写的error: false和error: true推断成了宽泛的boolean类型,而不是你定义的字面量类型false/true。
你定义的MyResponseSuccess和MyResponseFailure是个可区分联合类型——靠error字段的字面量值来区分成功和失败的响应。但在fetch的回调里,TypeScript默认会把error: false这种写法的类型推断成boolean,而不是精确的字面量false;同理error: true也会被推断为boolean。
这就导致你返回的Promise实际类型变成了Promise<{ data: any; error: boolean; } | { error: boolean; message: any; }>,和你声明的Promise<MyResponse>完全不兼容:比如{ data: any; error: boolean; }可能存在error: true的情况,这时候它既不符合MyResponseSuccess(要求error必须是false),也不符合MyResponseFailure(缺少必填的message字段),所以TypeScript抛出了类型不匹配的错误。
这里有几种简单可行的方案,选你觉得顺手的用就行:
方案1:用const断言锁定字面量类型
在每个分支的error字段后面加上as const,强制TypeScript把它推断成精确的字面量类型:
type MyResponseSuccess = { data: any, error: false }; type MyResponseFailure = { message: string, error: true }; type MyResponse = MyResponseSuccess | MyResponseFailure; function call(): Promise<MyResponse> { return fetch('ENDPOINT') .then(res => res.json()) .then(payload => ({ data: payload, error: false as const })) .catch(err => ({ error: true as const, message: err.toString() })); }
方案2:显式断言为对应的联合类型成员
直接把每个分支返回的对象断言成MyResponseSuccess或MyResponseFailure,明确告诉TypeScript这个对象就是对应的类型:
type MyResponseSuccess = { data: any, error: false }; type MyResponseFailure = { message: string, error: true }; type MyResponse = MyResponseSuccess | MyResponseFailure; function call(): Promise<MyResponse> { return fetch('ENDPOINT') .then(res => res.json()) .then(payload => ({ data: payload, error: false } as MyResponseSuccess)) .catch(err => ({ error: true, message: err.toString() } as MyResponseFailure)); }
方案3:封装成功/失败的返回函数
如果这种响应格式会多次用到,可以封装成专门的函数,让TypeScript自动推断正确的类型:
type MyResponseSuccess = { data: any, error: false }; type MyResponseFailure = { message: string, error: true }; type MyResponse = MyResponseSuccess | MyResponseFailure; const createSuccessResponse = (data: any): MyResponseSuccess => ({ data, error: false }); const createFailureResponse = (message: string): MyResponseFailure => ({ error: true, message }); function call(): Promise<MyResponse> { return fetch('ENDPOINT') .then(res => res.json()) .then(payload => createSuccessResponse(payload)) .catch(err => createFailureResponse(err.toString())); }
这三种方案都能让TypeScript正确识别返回值的类型,完美匹配你定义的MyResponse联合类型。
内容的提问来源于stack exchange,提问作者kuy

