TypeScript映射MongoDB可选_id字段时的类型错误问题咨询
问题分析与解决方案
为什么会出现类型推断缺失id的情况?
TypeScript对函数返回值的类型推断逻辑是取所有分支返回类型的交集。你的函数有两个返回分支:
- 当
_id不存在时,返回Omit<T, "_id">(仅保留原对象除_id外的属性) - 当
_id存在时,返回Omit<T, "_id"> & {id: string}(保留原属性并新增id)
由于交集只会保留所有分支共有的属性,id只存在于其中一个分支,因此TS最终推断的返回类型仅为Omit<T, "_id">,导致调用时无法访问id字段。
如何编写正确的类型定义?
需要通过显式条件类型来让TS根据输入对象是否包含_id,精准推断返回类型:
1. 定义映射工具类型
先创建一个工具类型,用于根据输入类型T生成对应的输出类型:
type MapId<T> = T extends { _id: infer U } ? Omit<T, "_id"> & { id: U } : Omit<T, "_id">;
这个类型的逻辑是:
- 如果
T包含必填的_id字段,就移除_id并新增同类型的id字段 - 如果
T不包含_id,则直接返回移除_id后的类型(和原类型一致)
2. 修改函数并指定返回类型
给函数显式指定返回类型为MapId<T>,同时用类型断言告诉TS每个分支的返回值符合该类型:
const mapId = <T extends { _id?: string }>(item: T): MapId<T> => { const { _id, ...rest } = item; if (_id === undefined) { return rest as MapId<T>; } return { ...rest, id: _id } as MapId<T>; };
3. 测试验证
现在调用函数时,TS会根据输入对象的结构正确推断返回类型:
// 包含_id的对象,返回类型会有id字段 const city = { _id: 'new-york', name: 'New York' }; const mappedCity = mapId(city); console.log(mappedCity.id); // 无TS错误 // 不包含_id的对象,返回类型无id字段 const noIdCity = { name: 'London' }; const mappedNoIdCity = mapId(noIdCity); console.log(mappedNoIdCity.id); // TS会提示"id不存在",符合预期
扩展支持MongoDB ObjectId
如果需要处理MongoDB的ObjectId类型,只需调整泛型约束:
import { ObjectId } from 'mongodb'; type MapId<T> = T extends { _id: infer U } ? Omit<T, "_id"> & { id: U } : Omit<T, "_id">; const mapId = <T extends { _id?: string | ObjectId }>(item: T): MapId<T> => { const { _id, ...rest } = item; if (_id === undefined) { return rest as MapId<T>; } return { ...rest, id: _id } as MapId<T>; };
内容的提问来源于stack exchange,提问作者hskris
相关产品推荐
相关产品推荐

