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)
  • @types/validator
  • @types/bluebird

Usage

Example of a minimal TypeScript project:

  1. import { Sequelize, Model, DataTypes, BuildOptions } from 'sequelize';
  2. import { HasManyGetAssociationsMixin, HasManyAddAssociationMixin, HasManyHasAssociationMixin, Association, HasManyCountAssociationsMixin, HasManyCreateAssociationMixin } from 'sequelize';
  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. // timestamps!
  8. public readonly createdAt!: Date;
  9. public readonly updatedAt!: Date;
  10. // Since TS cannot determine model association at compile time
  11. // we have to declare them here purely virtually
  12. // these will not exist until `Model.init` was called.
  13. public getProjects!: HasManyGetAssociationsMixin<Project>; // Note the null assertions!
  14. public addProject!: HasManyAddAssociationMixin<Project, number>;
  15. public hasProject!: HasManyHasAssociationMixin<Project, number>;
  16. public countProjects!: HasManyCountAssociationsMixin;
  17. public createProject!: HasManyCreateAssociationMixin<Project>;
  18. // You can also pre-declare possible inclusions, these will only be populated if you
  19. // actively include a relation.
  20. public readonly projects?: Project[]; // Note this is optional since it's only populated when explicitly requested in code
  21. public static associations: {
  22. projects: Association<User, Project>;
  23. };
  24. }
  25. const sequelize = new Sequelize('mysql://root:asd123@localhost:3306/mydb');
  26. class Project extends Model {
  27. public id!: number;
  28. public ownerId!: number;
  29. public name!: string;
  30. public readonly createdAt!: Date;
  31. public readonly updatedAt!: Date;
  32. }
  33. class Address extends Model {
  34. public userId!: number;
  35. public address!: string;
  36. public readonly createdAt!: Date;
  37. public readonly updatedAt!: Date;
  38. }
  39. Project.init({
  40. id: {
  41. type: DataTypes.INTEGER.UNSIGNED, // you can omit the `new` but this is discouraged
  42. autoIncrement: true,
  43. primaryKey: true,
  44. },
  45. ownerId: {
  46. type: DataTypes.INTEGER.UNSIGNED,
  47. allowNull: false,
  48. },
  49. name: {
  50. type: new DataTypes.STRING(128),
  51. allowNull: false,
  52. }
  53. }, {
  54. sequelize,
  55. tableName: 'projects',
  56. });
  57. User.init({
  58. id: {
  59. type: DataTypes.INTEGER.UNSIGNED,
  60. autoIncrement: true,
  61. primaryKey: true,
  62. },
  63. name: {
  64. type: new DataTypes.STRING(128),
  65. allowNull: false,
  66. },
  67. preferredName: {
  68. type: new DataTypes.STRING(128),
  69. allowNull: true
  70. }
  71. }, {
  72. tableName: 'users',
  73. sequelize: sequelize, // this bit is important
  74. });
  75. Address.init({
  76. userId: {
  77. type: DataTypes.INTEGER.UNSIGNED,
  78. },
  79. address: {
  80. type: new DataTypes.STRING(128),
  81. allowNull: false,
  82. }
  83. }, {
  84. tableName: 'address',
  85. sequelize: sequelize, // this bit is important
  86. });
  87. // Here we associate which actually populates out pre-declared `association` static and other methods.
  88. User.hasMany(Project, {
  89. sourceKey: 'id',
  90. foreignKey: 'ownerId',
  91. as: 'projects' // this determines the name in `associations`!
  92. });
  93. Address.belongsTo(User, {targetKey: 'id'});
  94. User.hasOne(Address,{sourceKey: 'id'});
  95. async function stuff() {
  96. // Please note that when using async/await you lose the `bluebird` promise context
  97. // and you fall back to native
  98. const newUser = await User.create({
  99. name: 'Johnny',
  100. preferredName: 'John',
  101. });
  102. console.log(newUser.id, newUser.name, newUser.preferredName);
  103. const project = await newUser.createProject({
  104. name: 'first!',
  105. });
  106. const ourUser = await User.findByPk(1, {
  107. include: [User.associations.projects],
  108. rejectOnEmpty: true, // Specifying true here removes `null` from the return type!
  109. });
  110. console.log(ourUser.projects![0].name); // Note the `!` null assertion since TS can't know if we included
  111. // the model or not
  112. }

Usage of sequelize.define

TypeScript doesn't know how to generate a class definition when we use the sequelize.define method to define a Model. Therefore, we need to do some manual work and declare an interface and a type, and eventually cast the result of .define to the static type.

  1. // We need to declare an interface for our model that is basically what our class would be
  2. interface MyModel extends Model {
  3. readonly id: number;
  4. }
  5. // Need to declare the static model so `findOne` etc. use correct types.
  6. type MyModelStatic = typeof Model & {
  7. new (values?: object, options?: BuildOptions): MyModel;
  8. }
  9. // TS can't derive a proper class definition from a `.define` call, therefor we need to cast here.
  10. const MyDefineModel = <MyModelStatic>sequelize.define('MyDefineModel', {
  11. id: {
  12. primaryKey: true,
  13. type: DataTypes.INTEGER.UNSIGNED,
  14. }
  15. });
  16. function stuffTwo() {
  17. MyDefineModel.findByPk(1, {
  18. rejectOnEmpty: true,
  19. })
  20. .then(myModel => {
  21. console.log(myModel.id);
  22. });
  23. }