TypeScript

Since v5, Sequelize provides its own TypeScript definitions. Please note that only TS >= 3.1 is supported.

As Sequelize heavily relies on runtime property assignments, TypeScript won’t be very useful out of the box. A decent amount of manual type declarations are needed to make models workable.

Installation

In order to avoid installation bloat for non TS users, you must install the following typing packages manually:

  • @types/node (this is universally required in node projects)
  • @types/validator

Usage

Example of a minimal TypeScript project with strict type-checking for attributes.

NOTE: Keep the following code in sync with /types/test/typescriptDocs/ModelInit.ts to ensure it typechecks correctly.

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

Usage without strict types for attributes

The typings for Sequelize v5 allowed you to define models without specifying types for the attributes. This is still possible for backwards compatibility and for cases where you feel strict typing for attributes isn’t worth it.

NOTE: Keep the following code in sync with typescriptDocs/ModelInitNoAttributes.ts to ensure it typechecks correctly.

  1. import { Sequelize, Model, DataTypes } from "sequelize";
  2. const sequelize = new Sequelize("mysql://root:[email protected]: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. }

Usage of sequelize.define

In Sequelize versions before v5, the default way of defining a model involved using sequelize.define. It’s still possible to define models with that, and you can also add typings to these models using interfaces.

NOTE: Keep the following code in sync with typescriptDocs/Define.ts to ensure it typechecks correctly.

  1. import { Sequelize, Model, DataTypes, Optional } from "sequelize";
  2. const sequelize = new Sequelize("mysql://root:[email protected]: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. }

If you’re comfortable with somewhat less strict typing for the attributes on a model, you can save some code by defining the Instance to just extend Model without any attributes in the generic types.

NOTE: Keep the following code in sync with typescriptDocs/DefineNoAttributes.ts to ensure it typechecks correctly.

  1. import { Sequelize, Model, DataTypes } from "sequelize";
  2. const sequelize = new Sequelize("mysql://root:[email protected]: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. }