添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
跳到主要内容

TypeScript

Sequelize 提供了自己的 TypeScript 定义.

请注意, 仅支持 TypeScript >= 4.1 . 我们对 TypeScript 的支持不遵循 SemVer. 我们将支持 TypeScript 版本至少一年, 之后它们可能会在 SemVer MINOR 版本中被删除.

由于 Sequelize 严重依赖于运行时属性分配, 因此 TypeScript 无法立即开箱即用.

为了使模型可用, 需要大量的手动类型声明.

安装

为了避免与不同的 Node 版本发生冲突. 你必须手动安装 @types/node .

使用

重要 : 您必须在类属性类型上使用 declare 以确保 TypeScript 不会触发这些类属性. 参阅 公共类字段的注意事项

Sequelize Models 接受两种通用类型来定义模型的属性和创建属性是什么样的:

import { Model, Optional } from 'sequelize';

// We don't recommend doing this. Read on for the new way of declaring Model typings.

type UserAttributes = {
id: number,
name: string,
// other attributes...
};

// we're telling the Model that 'id' is optional
// when creating an instance of the model (such as using Model.create()).
type UserCreationAttributes = Optional<UserAttributes, 'id'>;

class User extends Model<UserAttributes, UserCreationAttributes> {
declare id: number;
declare name: string;
// other attributes...
}

这个解决方案很冗长. Sequelize >=6.14.0 提供了新的实用程序类型, 将大大减少数量 必需的样板: InferAttributes InferCreationAttributes . 他们将提取属性类型 直接来自模型:

import { Model, InferAttributes, InferCreationAttributes, CreationOptional } from 'sequelize';

// order of InferAttributes & InferCreationAttributes is important.
class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
// 'CreationOptional' is a special type that marks the field as optional
// when creating an instance of the model (such as using Model.create()).
declare id: CreationOptional<number>;
declare name: string;
// other attributes...
}

关于 InferAttributes InferCreationAttributes 工作需要了解的重要事项: 它们将选择类的所有声明属性,除了:

  • 静态字段和方法.
  • 方法(任何类型为函数的东西).
  • 类型使用铭记类型 NonAttribute 的那些.
  • 像这样使用 InferAttributes 排除的那些: InferAttributes<User, { omit: 'properties' | 'to' | 'omit' }> .
  • 由 Model 超类声明的那些(但不是中间类!). 如果您的属性之一与 Model 的属性之一同名, 请更改其名称. 无论如何, 这样做可能会导致问题.
  • Getter & setter 不会被自动排除. 将它们的 return / parameter 类型设置为 NonAttribute , 或将它们添加到 omit 以排除它们.

InferCreationAttributes 的工作方式与 InferAttributes 相同, 但有一个例外: 使用 CreationOptional 类型键入的属性将被标记为可选.

请注意,接受 null undefined 的属性不需要使用 CreationOptional :

class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
declare firstName: string;

// 不需要在 lastName 上使用 CreationOptional
// 因为可为 null 的属性在 User.create() 中始终是可选的
declare lastName: string | null;
}

// ...

await User.create({
firstName: 'Zoé',
// 省略姓氏, 但这仍然有效!
});

您只需要在类实例字段或 getter 上使用 CreationOptional NonAttribute .

对属性进行严格类型检查的最小 TypeScript 项目示例:

import {
Association, DataTypes, HasManyAddAssociationMixin, HasManyCountAssociationsMixin,
HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, HasManyHasAssociationMixin,
HasManySetAssociationsMixin, HasManyAddAssociationsMixin, HasManyHasAssociationsMixin,
HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin, Model, ModelDefined, Optional,
Sequelize, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute
} from 'sequelize';

const sequelize = new Sequelize('mysql://root:asd123@localhost:3306/mydb');

// 'projects' is excluded as it's not an attribute, it's an association.
class User extends Model<InferAttributes<User, { omit: 'projects' }>, InferCreationAttributes<User, { omit: 'projects' }>> {
// id can be undefined during creation when using `autoIncrement`
declare id: CreationOptional<number>;
declare name: string;
declare preferredName: string | null; // for nullable fields

// timestamps!
// createdAt can be undefined during creation
declare createdAt: CreationOptional<Date>;
// updatedAt can be undefined during creation
declare updatedAt: CreationOptional<Date>;

// Since TS cannot determine model association at compile time
// we have to declare them here purely virtually
// these will not exist until `Model.init` was called.
declare getProjects: HasManyGetAssociationsMixin<Project>; // Note the null assertions!
declare addProject: HasManyAddAssociationMixin<Project, number>;
declare addProjects: HasManyAddAssociationsMixin<Project, number>;
declare setProjects: HasManySetAssociationsMixin<Project, number>;
declare removeProject: HasManyRemoveAssociationMixin<Project, number>;
declare removeProjects: HasManyRemoveAssociationsMixin<Project, number>;
declare hasProject: HasManyHasAssociationMixin<Project, number>;
declare hasProjects: HasManyHasAssociationsMixin<Project, number>;
declare countProjects: HasManyCountAssociationsMixin;
declare createProject: HasManyCreateAssociationMixin<Project, 'ownerId'>;

// You can also pre-declare possible inclusions, these will only be populated if you
// actively include a relation.
declare projects?: NonAttribute<Project[]>; // Note this is optional since it's only populated when explicitly requested in code

// getters that are not attributes should be tagged using NonAttribute
// to remove them from the model's Attribute Typings.
get fullName(): NonAttribute<string> {
return this.name;
}

declare static associations: {
projects: Association<User, Project>;
};
}

class Project extends Model<
InferAttributes<Project>,
InferCreationAttributes<Project>
> {
// id can be undefined during creation when using `autoIncrement`
declare id: CreationOptional<number>;
declare ownerId: number;
declare name: string;

// `owner` is an eagerly-loaded association.
// We tag it as `NonAttribute`
declare owner?: NonAttribute<User>;

// createdAt can be undefined during creation
declare createdAt: CreationOptional<Date>;
// updatedAt can be undefined during creation
declare updatedAt: CreationOptional<Date>;
}

class Address extends Model<
InferAttributes<Address>,
InferCreationAttributes<Address>
> {
declare userId: number;
declare address: string;

// createdAt can be undefined during creation
declare createdAt: CreationOptional<Date>;
// updatedAt can be undefined during creation
declare updatedAt: CreationOptional<Date>;
}

Project.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
ownerId: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false
},
name: {
type: new DataTypes.STRING(128),
allowNull: false
},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{
sequelize,
tableName: 'projects'
}
);

User.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
name: {
type: new DataTypes.STRING(128),
allowNull: false
},
preferredName: {
type: new DataTypes.STRING(128),
allowNull: true
},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{
tableName: 'users',
sequelize // passing the `sequelize` instance is required
}
);

Address.init(
{
userId: {
type: DataTypes.INTEGER.UNSIGNED
},
address: {
type: new DataTypes.STRING(128),
allowNull: false
},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{
tableName: 'address',
sequelize // passing the `sequelize` instance is required
}
);

// You can also define modules in a functional way
interface NoteAttributes {
id: number;
title: string;
content: string;
}

// You can also set multiple attributes optional at once
type NoteCreationAttributes = Optional<NoteAttributes, 'id' | 'title'>;

// And with a functional approach defining a module looks like this
const Note: ModelDefined<
NoteAttributes,
NoteCreationAttributes
> = sequelize.define(
'Note',
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
title: {
type: new DataTypes.STRING(64),
defaultValue: 'Unnamed Note'
},
content: {
type: new DataTypes.STRING(4096),
allowNull: false
}
},
{
tableName: 'notes'
}
);

// Here we associate which actually populates out pre-declared `association` static and other methods.
User.hasMany(Project, {
sourceKey: 'id',
foreignKey: 'ownerId',
as: 'projects' // this determines the name in `associations`!
});

Address.belongsTo(User, { targetKey: 'id' });
User.hasOne(Address, { sourceKey: 'id' });

async function doStuffWithUser() {
const newUser = await User.create({
name: 'Johnny',
preferredName: 'John',
});
console.log(newUser.id, newUser.name, newUser.preferredName);

const project = await newUser.createProject({
name: 'first!'
});

const ourUser = await User.findByPk(1, {
include: [User.associations.projects],
rejectOnEmpty: true // Specifying true here removes `null` from the return type!
});

// Note the `!` null assertion since TS can't know if we included
// the model or not
console.log(ourUser.projects![0].name);
}