TypeScript中如何根据字段存在性收窄对象类型?
TypeScript 入参类型自动收窄实现方案
问题场景
有两个功能相近的后端接口:一个用于获取全量列表,另一个用于获取过滤后的列表。因后端暂不支持将二者合并为携带查询参数的单个接口,因此封装统一请求函数,根据入参中id字段是否存在选择调用对应接口。但开发时无法获得正确的类型推断结果,入参args无法被TypeScript自动收窄为对应接口要求的参数类型。
先后尝试两种实现方案均存在类型报错,判断条件使用args.id !== undefined或'id' in args都无法收窄args的类型,两种失败实现示例如下:
方案一:提前声明联合类型函数变量
export interface Arguments { id?: number; token?: string; pageSize: number; } function requestTo1stEndpoint(a: { id: number; token?: string; pageSize: number; }) { return Promise.resolve(1); } function requestTo2ndEndpoint(a: { token?: string; pageSize: number; }) { return Promise.resolve(2); } async function request(args: Arguments) { let fun: typeof requestTo1stEndpoint | typeof requestTo2ndEndpoint if (args.id !== undefined) { fun = requestTo1stEndpoint } else { fun = requestTo2ndEndpoint } const response = await fun(args) // 此处报类型错误 }
方案二:分支内直接调用接口
async function request(args: Arguments) { let responsePromise: Promise<number> // 换用 'id' in args 作为判断条件也不生效 if (args.id !== undefined) { responsePromise = requestTo1stEndpoint(args) // 此处args类型无法收窄,报类型错误 } else { responsePromise = requestTo2ndEndpoint(args) } const response = await responsePromise }
问题原因
原Arguments接口将id定义为可选属性,这种结构下TypeScript无法通过id的存在性对整个对象类型做收窄:判断args.id !== undefined仅会把id字段本身的类型从number | undefined收窄为number,不会将整个对象识别为"必须携带id的接口参数类型",因此传参时会出现类型不匹配。
解决方法
不需要新增额外的判别字段,直接将入参定义为判别联合类型,以id字段的存在性作为判别依据即可实现自动类型收窄,实现代码如下:
// 过滤列表接口参数:id为必填项 type FilteredArgs = { id: number; token?: string; pageSize: number; } // 全量列表接口参数:id不存在 type FullListArgs = { id?: undefined; token?: string; pageSize: number; } // 统一入参为两种类型的联合 type Arguments = FilteredArgs | FullListArgs function requestTo1stEndpoint(a: FilteredArgs) { return Promise.resolve(1); } function requestTo2ndEndpoint(a: FullListArgs) { return Promise.resolve(2); } async function request(args: Arguments) { if (args.id !== undefined) { // 此处TS自动将args收窄为FilteredArgs类型,无类型报错 return requestTo1stEndpoint(args) } // 此处TS自动将args收窄为FullListArgs类型,无类型报错 return requestTo2ndEndpoint(args) }
实现说明
- 联合类型中通过
id?: undefined明确全量列表参数场景下id字段不存在,TS可以通过id的取值判断整个对象的类型 - 不需要提前声明联合类型的请求函数变量再统一调用,直接在分支内调用对应接口即可获得完整类型校验
- 该方案完全复用原有业务判断逻辑,不需要新增额外字段,对业务代码无侵入
内容的提问来源于stack exchange,提问作者vyenkv
相关产品推荐
相关产品推荐

