TypeScript

从 v5 开始,Sequelize 提供了自己的 TypeScript 定义. 请注意,仅支持 TS >= 3.1.

由于 Sequelize 严重依赖于运行时属性分配,因此 TypeScript 在开箱即用时不会很有用. 为了使模型可用,需要大量的手动类型声明.

安装

为了避免非 TS 用户的安装膨胀,你必须手动安装以下键入程序包:

  • @types/node (在 node 项目中这是通常是必须的)
  • @types/validator

使用

带有严格类型检查的最小 TypeScript 项目示例.

  1. import {
  2. Sequelize,
  3. Model,
  4. DataTypes,
  5. HasManyGetAssociationsMixin,
  6. HasManyAddAssociationMixin,
  7. HasManyHasAssociationMixin,
  8. Association,
  9. HasManyCountAssociationsMixin,
  10. HasManyCreateAssociationMixin,
  11. Optional,
  12. } from 'sequelize';
  13. const sequelize = new Sequelize('mysql://root:asd123@localhost:3306/mydb');
  14. // These are all the attributes in the User model
  15. interface UserAttributes {
  16. id: number;
  17. name: string;
  18. preferredName: string | null;
  19. }
  20. // Some attributes are optional in `User.build` and `User.create` calls
  21. interface UserCreationAttributes extends Optional<UserAttributes, 'id'> {}
  22. class User extends Model<UserAttributes, UserCreationAttributes>
  23. implements UserAttributes {
  24. public id!: number; // Note that the `null assertion` `!` is required in strict mode.
  25. public name!: string;
  26. public preferredName!: string | null; // for nullable fields
  27. // timestamps!
  28. public readonly createdAt!: Date;
  29. public readonly updatedAt!: Date;
  30. // Since TS cannot determine model association at compile time
  31. // we have to declare them here purely virtually
  32. // these will not exist until `Model.init` was called.
  33. public getProjects!: HasManyGetAssociationsMixin<Project>; // Note the null assertions!
  34. public addProject!: HasManyAddAssociationMixin<Project, number>;
  35. public hasProject!: HasManyHasAssociationMixin<Project, number>;
  36. public countProjects!: HasManyCountAssociationsMixin;
  37. public createProject!: HasManyCreateAssociationMixin<Project>;
  38. // You can also pre-declare possible inclusions, these will only be populated if you
  39. // actively include a relation.
  40. public readonly projects?: Project[]; // Note this is optional since it's only populated when explicitly requested in code
  41. public static associations: {
  42. projects: Association<User, Project>;
  43. };
  44. }
  45. interface ProjectAttributes {
  46. id: number;
  47. ownerId: number;
  48. name: string;
  49. }
  50. interface ProjectCreationAttributes extends Optional<ProjectAttributes, 'id'> {}
  51. class Project extends Model<ProjectAttributes, ProjectCreationAttributes>
  52. implements ProjectAttributes {
  53. public id!: number;
  54. public ownerId!: number;
  55. public name!: string;
  56. public readonly createdAt!: Date;
  57. public readonly updatedAt!: Date;
  58. }
  59. interface AddressAttributes {
  60. userId: number;
  61. address: string;
  62. }
  63. // You can write `extends Model<AddressAttributes, AddressAttributes>` instead,
  64. // but that will do the exact same thing as below
  65. class Address extends Model<AddressAttributes> implements AddressAttributes {
  66. public userId!: number;
  67. public address!: string;
  68. public readonly createdAt!: Date;
  69. public readonly updatedAt!: Date;
  70. }
  71. Project.init(
  72. {
  73. id: {
  74. type: DataTypes.INTEGER.UNSIGNED,
  75. autoIncrement: true,
  76. primaryKey: true,
  77. },
  78. ownerId: {
  79. type: DataTypes.INTEGER.UNSIGNED,
  80. allowNull: false,
  81. },
  82. name: {
  83. type: new DataTypes.STRING(128),
  84. allowNull: false,
  85. },
  86. },
  87. {
  88. sequelize,
  89. tableName: 'projects',
  90. },
  91. );
  92. User.init(
  93. {
  94. id: {
  95. type: DataTypes.INTEGER.UNSIGNED,
  96. autoIncrement: true,
  97. primaryKey: true,
  98. },
  99. name: {
  100. type: new DataTypes.STRING(128),
  101. allowNull: false,
  102. },
  103. preferredName: {
  104. type: new DataTypes.STRING(128),
  105. allowNull: true,
  106. },
  107. },
  108. {
  109. tableName: 'users',
  110. sequelize, // passing the `sequelize` instance is required
  111. },
  112. );
  113. Address.init(
  114. {
  115. userId: {
  116. type: DataTypes.INTEGER.UNSIGNED,
  117. },
  118. address: {
  119. type: new DataTypes.STRING(128),
  120. allowNull: false,
  121. },
  122. },
  123. {
  124. tableName: 'address',
  125. sequelize, // passing the `sequelize` instance is required
  126. },
  127. );
  128. // Here we associate which actually populates out pre-declared `association` static and other methods.
  129. User.hasMany(Project, {
  130. sourceKey: 'id',
  131. foreignKey: 'ownerId',
  132. as: 'projects', // this determines the name in `associations`!
  133. });
  134. Address.belongsTo(User, { targetKey: 'id' });
  135. User.hasOne(Address, { sourceKey: 'id' });
  136. async function doStuffWithUser() {
  137. const newUser = await User.create({
  138. name: 'Johnny',
  139. preferredName: 'John',
  140. });
  141. console.log(newUser.id, newUser.name, newUser.preferredName);
  142. const project = await newUser.createProject({
  143. name: 'first!',
  144. });
  145. const ourUser = await User.findByPk(1, {
  146. include: [User.associations.projects],
  147. rejectOnEmpty: true, // Specifying true here removes `null` from the return type!
  148. });
  149. // Note the `!` null assertion since TS can't know if we included
  150. // the model or not
  151. console.log(ourUser.projects![0].name);
  152. }

使用非严格类型

Sequelize v5 的类型允许你定义模型而无需指定属性类型. 对于向后兼容以及在你觉得对属性进行严格检查是不值得的情况下, 这仍然是可行的.

  1. import { Sequelize, Model, DataTypes } from 'sequelize';
  2. const sequelize = new Sequelize('mysql://root:asd123@localhost:3306/mydb');
  3. class User extends Model {
  4. public id!: number; // Note that the `null assertion` `!` is required in strict mode.
  5. public name!: string;
  6. public preferredName!: string | null; // for nullable fields
  7. }
  8. User.init(
  9. {
  10. id: {
  11. type: DataTypes.INTEGER.UNSIGNED,
  12. autoIncrement: true,
  13. primaryKey: true,
  14. },
  15. name: {
  16. type: new DataTypes.STRING(128),
  17. allowNull: false,
  18. },
  19. preferredName: {
  20. type: new DataTypes.STRING(128),
  21. allowNull: true,
  22. },
  23. },
  24. {
  25. tableName: 'users',
  26. sequelize, // passing the `sequelize` instance is required
  27. },
  28. );
  29. async function doStuffWithUserModel() {
  30. const newUser = await User.create({
  31. name: 'Johnny',
  32. preferredName: 'John',
  33. });
  34. console.log(newUser.id, newUser.name, newUser.preferredName);
  35. const foundUser = await User.findOne({ where: { name: 'Johnny' } });
  36. if (foundUser === null) return;
  37. console.log(foundUser.name);
  38. }

使用 sequelize.define

在 v5 之前的 Sequelize 版本中, 定义模型的默认方式涉及使用 sequelize.define. 仍然可以使用它来定义模型, 也可以使用接口在这些模型中添加类型.

  1. import { Sequelize, Model, DataTypes, Optional } from 'sequelize';
  2. const sequelize = new Sequelize('mysql://root:asd123@localhost:3306/mydb');
  3. // We recommend you declare an interface for the attributes, for stricter typechecking
  4. interface UserAttributes {
  5. id: number;
  6. name: string;
  7. }
  8. // Some fields are optional when calling UserModel.create() or UserModel.build()
  9. interface UserCreationAttributes extends Optional<UserAttributes, 'id'> {}
  10. // We need to declare an interface for our model that is basically what our class would be
  11. interface UserInstance
  12. extends Model<UserAttributes, UserCreationAttributes>,
  13. UserAttributes {}
  14. const UserModel = sequelize.define<UserInstance>('User', {
  15. id: {
  16. primaryKey: true,
  17. type: DataTypes.INTEGER.UNSIGNED,
  18. },
  19. name: {
  20. type: DataTypes.STRING,
  21. }
  22. });
  23. async function doStuff() {
  24. const instance = await UserModel.findByPk(1, {
  25. rejectOnEmpty: true,
  26. });
  27. console.log(instance.id);
  28. }

如果你对模型上非严格的属性检查命令感到满意,则可以通过定义 Instance 来扩展 Model 而无需泛型类型中的任何属性, 从而节省一些代码.

  1. import { Sequelize, Model, DataTypes } from 'sequelize';
  2. const sequelize = new Sequelize('mysql://root:asd123@localhost:3306/mydb');
  3. // We need to declare an interface for our model that is basically what our class would be
  4. interface UserInstance extends Model {
  5. id: number;
  6. name: string;
  7. }
  8. const UserModel = sequelize.define<UserInstance>('User', {
  9. id: {
  10. primaryKey: true,
  11. type: DataTypes.INTEGER.UNSIGNED,
  12. },
  13. name: {
  14. type: DataTypes.STRING,
  15. },
  16. });
  17. async function doStuff() {
  18. const instance = await UserModel.findByPk(1, {
  19. rejectOnEmpty: true,
  20. });
  21. console.log(instance.id);
  22. }