TypeScript中Sequelize getter/setter与字段DataType类型不匹配问询
环境信息
- Sequelize版本:
6.6.5 - Node.js版本:
14.17.6 - TypeScript版本:
4.4.3 - 数据库方言:
sqlite
报错原因
Sequelize 6 的 TypeScript 类型设计中,getDataValue、setDataValue 方法以及实例公开属性的类型,完全继承传入 Model 泛型的首个参数(也就是你定义的 IUserAttributes),并不会自动识别列定义的 DataType 来推导实际存储类型。所以你在接口中将 registeredAtIp 声明为 string 后,TS 会默认该字段所有相关操作的类型都是 string,和数据库实际存储的数值类型无关,因此会抛出类型不匹配的错误。
解决方案
以下两种方案都无需使用强制类型断言,同时可以保留实例对外暴露的 string 类型,创建实例时可正常传入字符串格式的IP:
方案1:显式声明dataValues类型(单字段转换场景首选)
Sequelize 所有字段的原始存储值都保存在实例的 dataValues 属性中,你只需要在模型类中单独声明该属性的实际类型,覆盖默认的泛型推导即可:
interface IUserAttributes { id: number; registeredAtIp: string; } interface IUserCreationAttributes extends Optional<IUserAttributes, 'id'> {} class User extends Model<IUserAttributes, IUserCreationAttributes> implements IUserAttributes { public id!: number; public registeredAtIp!: string; // 新增声明:指定dataValues中registeredAtIp的实际存储类型为number declare readonly dataValues: Omit<IUserAttributes, 'registeredAtIp'> & { registeredAtIp: number }; }
调整后在getter/setter中直接读写 this.dataValues.registeredAtIp 就会被自动推断为number类型,不会再触发类型报错。
方案2:拆分存储类型和业务类型(多字段转换场景首选)
如果有多个字段都需要做内外类型转换,可以拆分两个接口分别定义数据库存储类型和对外暴露的业务类型,维护性更高:
// 数据库实际存储的原始字段类型 interface IUserDbAttributes { id: number; registeredAtIp: number; } // 对外暴露的业务字段类型 interface IUserAttributes extends Omit<IUserDbAttributes, 'registeredAtIp'> { registeredAtIp: string; } interface IUserCreationAttributes extends Optional<IUserAttributes, 'id'> {} class User extends Model<IUserAttributes, IUserCreationAttributes> implements IUserAttributes { public id!: number; public registeredAtIp!: string; declare readonly dataValues: IUserDbAttributes; }
后续新增需要类型转换的字段时,只需要在两个接口中分别声明对应类型即可,逻辑更清晰。
内容的提问来源于stack exchange,提问作者user16238654
相关产品推荐
相关产品推荐

