Sequelize中获取可选关联模型的普通对象时,如何修正TypeScript属性类型推断?
Sequelize中获取可选关联模型的普通对象时,如何修正TypeScript属性类型推断?
这确实是Sequelize TypeScript类型系统里一个常见的痛点——当你用.get({plain: true})获取扁平化的普通对象时,嵌套的关联属性(尤其是可选的)类型不会自动递归转换成Attributes<Model>类型,而是保留了原Model实例类型,导致TypeScript推断和实际返回的普通对象结构不匹配。
问题根源
你自己尝试的RecurseAttributes类型有两个核心问题:
- 错误地使用了
M[key] extends Model进行判断——M[key]指向的是Model实例类型(比如Category),但我们需要处理的是Attributes<M>中的属性类型(也就是Category | undefined这种可选关联类型) - 没有处理可选属性的
undefined分支,导致TypeScript无法识别可选属性的类型转换逻辑
解决方案:修正递归类型定义
我们需要重新定义一个递归类型,它会遍历模型的Attributes结构、处理可选性、递归转换所有嵌套的Model类型为对应的Attributes类型:
import { Model, Attributes } from 'sequelize'; // 递归将所有嵌套的Model类型转换为Attributes类型,同时保留原属性的可选性和undefined export type RecursiveAttributes<M extends Model> = { [K in keyof Attributes<M>]: // 处理数组类型的关联(比如hasMany返回的Model[]) Attributes<M>[K] extends Array<infer SubModel extends Model> ? RecursiveAttributes<SubModel>[] // 处理可选/非可选的单个Model关联 : Attributes<M>[K] extends Model | undefined ? RecursiveAttributes<Exclude<Attributes<M>[K], undefined>> | undefined // 非Model类型的属性直接保留原类型 : Attributes<M>[K] };
类型逻辑解释
- 遍历
Attributes<M>的所有属性键,确保我们基于普通对象的结构进行转换 - 先处理数组类型的关联(比如
hasMany返回的模型数组),将数组中的每个Model递归转换为Attributes类型 - 再处理单个Model关联(包括可选的
Model | undefined):- 用
Exclude<Attributes<M>[K], undefined>剔除undefined,拿到纯Model类型 - 递归应用
RecursiveAttributes转换该Model为Attributes类型 - 重新添加
undefined,保留原属性的可选性
- 用
- 非Model类型的属性直接保留原类型
实际使用示例
在你的代码中,只需要用这个类型进行类型断言即可:
// 加载数据(记得include关联,否则ParentCategory会是undefined) const category = await Category.findByPk(1234, { include: [{ model: Category, as: 'ParentCategory' }] }); // 获取普通对象并应用类型断言 const cat = category?.get({ plain: true }) as RecursiveAttributes<Category>; // 现在TypeScript会正确推断: // cat.ParentCategory → RecursiveAttributes<Category> | undefined // 完全等价于 Attributes<Category> | undefined,和实际返回的普通对象结构一致
扩展说明
这个类型不仅能处理你遇到的可选belongsTo关联,还能处理:
- 非可选的单个Model关联
hasMany返回的Model数组关联- 多层嵌套的关联(比如
Category → ParentCategory → GrandparentCategory这种深层嵌套)
内容来源于stack exchange
相关产品推荐
相关产品推荐

