教程 > sequelize 中文教程 > 阅读:92

sequelize 与 typescript——迹忆客-ag捕鱼王app官网

sequelize 提供了自己的 typescript 定义。

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

由于 sequelize 严重依赖于运行时属性分配, 因此 typescript 无法立即开箱即用。

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

安装

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

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

使用

重要 : 我们必须在类属性类型上使用 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;
class user extends model {
  declare id: number;
  declare string: number;
  // other attributes...
}

这个ag捕鱼王app官网的解决方案很冗长。 sequelize >=6.14.0 提供了新的实用程序类型, 将大大减少数量 必需的样板: inferattributesinfercreationattributes。 他们将提取属性类型 直接来自模型:

import { model, inferattributes, infercreationattributes, creationoptional } from 'sequelize';
// order of inferattributes & infercreationattributes is important.
class user extends model, infercreationattributes> {
  // '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;
  declare string: number;
  // other attributes...
}

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

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

infercreationattributes 的工作方式与 attributesof 相同, 但有一个例外: 使用 creationoptional 类型键入的属性将被标记为可选。

我们只需要在类实例字段或 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, infercreationattributes> {
  // id can be undefined during creation when using `autoincrement`
  declare id: creationoptional;
  declare name: string;
  declare preferredname: string | null; // for nullable fields
  // timestamps!
  // createdat can be undefined during creation
  declare createdat: creationoptional;
  // updatedat can be undefined during creation
  declare updatedat: creationoptional;
  // 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; // note the null assertions!
  declare addproject: hasmanyaddassociationmixin;
  declare addprojects: hasmanyaddassociationsmixin;
  declare setprojects: hasmanysetassociationsmixin;
  declare removeproject: hasmanyremoveassociationmixin;
  declare removeprojects: hasmanyremoveassociationsmixin;
  declare hasproject: hasmanyhasassociationmixin;
  declare hasprojects: hasmanyhasassociationsmixin;
  declare countprojects: hasmanycountassociationsmixin;
  declare createproject: hasmanycreateassociationmixin;
  // you can also pre-declare possible inclusions, these will only be populated if you
  // actively include a relation.
  declare projects?: nonattribute; // 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 {
    return this.name;
  }
  declare static associations: {
    projects: association;
  };
}
class project extends model<
  inferattributes,
  infercreationattributes
> {
  // id can be undefined during creation when using `autoincrement`
  declare id: creationoptional;
  declare ownerid: number;
  declare name: string;
  // `owner` is an eagerly-loaded association.
  // we tag it as `nonattribute`
  declare owner?: nonattribute;
  // createdat can be undefined during creation
  declare createdat: creationoptional;
  // updatedat can be undefined during creation
  declare updatedat: creationoptional;
}
class address extends model<
  inferattributes
, infercreationattributes
> { declare userid: number; declare address: string; // createdat can be undefined during creation declare createdat: creationoptional; // updatedat can be undefined during creation declare updatedat: creationoptional; } 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; // 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); } (async () => { await sequelize.sync(); await dostuffwithuser(); })();

使用非严格类型

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

import { sequelize, model, datatypes } from 'sequelize';
const sequelize = new sequelize('mysql://root:asd123@localhost:3306/mydb');
class user extends model {
  declare id: number;
  declare name: string;
  declare preferredname: string | null;
}
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,
    },
  },
  {
    tablename: 'users',
    sequelize, // passing the `sequelize` instance is required
  },
);
async function dostuffwithusermodel() {
  const newuser = await user.create({
    name: 'johnny',
    preferredname: 'john',
  });
  console.log(newuser.id, newuser.name, newuser.preferredname);
  const founduser = await user.findone({ where: { name: 'johnny' } });
  if (founduser === null) return;
  console.log(founduser.name);
}

使用 sequelize.define

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

import { sequelize, model, datatypes, optional } from 'sequelize';
const sequelize = new sequelize('mysql://root:asd123@localhost:3306/mydb');
// we recommend you declare an interface for the attributes, for stricter typechecking
interface userattributes {
  id: number;
  name: string;
}
// some fields are optional when calling usermodel.create() or usermodel.build()
interface usercreationattributes extends optional {}
// we need to declare an interface for our model that is basically what our class would be
interface userinstance
  extends model,
    userattributes {}
const usermodel = sequelize.define('user', {
  id: {
    primarykey: true,
    type: datatypes.integer.unsigned,
  },
  name: {
    type: datatypes.string,
  }
});
async function dostuff() {
  const instance = await usermodel.findbypk(1, {
    rejectonempty: true,
  });
  console.log(instance.id);
}

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

import { sequelize, model, datatypes } from 'sequelize';
const sequelize = new sequelize('mysql://root:asd123@localhost:3306/mydb');
// we need to declare an interface for our model that is basically what our class would be
interface userinstance extends model {
  id: number;
  name: string;
}
const usermodel = sequelize.define('user', {
  id: {
    primarykey: true,
    type: datatypes.integer.unsigned,
  },
  name: {
    type: datatypes.string,
  },
});
async function dostuff() {
  const instance = await usermodel.findbypk(1, {
    rejectonempty: true,
  });
  console.log(instance.id);
}

实用程序类型

请求模型类

modelstatic 旨在用于键入模型 class。

以下是请求模型类并返回该类中定义的主键列表的实用方法示例:

import { modelstatic, modelattributecolumnoptions, model, inferattributes, infercreationattributes, creationoptional } from 'sequelize';
/**
 * returns the list of attributes that are part of the model's primary key.
 */
export function getprimarykeyattributes(model: modelstatic): modelattributecolumnoptions[] {
  const attributes: modelattributecolumnoptions[] = [];
  for (const attribute of object.values(model.rawattributes)) {
    if (attribute.primarykey) {
      attributes.push(attribute);
    }
  }
  return attributes;
}
class user extends model, infercreationattributes> {
  id: creationoptional;
}
user.init({
  id: {
    type: datatypes.integer.unsigned,
    autoincrement: true,
    primarykey: true
  },
}, { sequelize });
const primaryattributes = getprimarykeyattributes(user);

获取模型的属性

如果您需要访问给定模型的属性列表, 你需要使用 attributescreationattributes

它们将返回作为参数传递的模型的属性(和创建属性)。

不要将它们与 inferattributesinfercreationattributes 混淆。 这两种实用程序类型应该只被使用 在模型的定义中自动从模型的公共类字段创建属性列表。 它们仅适用于基于类的模型定义(使用 model.init 时)。

attributescreationattributes 将返回任何模型的属性列表, 无论它们是如何创建的(无论是 model.init 还是 sequelize#define)。

这是一个请求模型类和属性名称的实用函数示例; 并返回相应的属性元数据。

import {
  modelstatic,
  modelattributecolumnoptions,
  model,
  inferattributes,
  infercreationattributes,
  creationoptional,
  attributes
} from 'sequelize';
export function getattributemetadata(model: modelstatic, attributename: keyof attributes): modelattributecolumnoptions {
  const attribute = model.rawattributes[attributename];
  if (attribute == null) {
    throw new error(`attribute ${attributename} does not exist on model ${model.name}`);
  }
  return attribute;
}
class user extends model, infercreationattributes> {
  id: creationoptional;
}
user.init({
  id: {
    type: datatypes.integer.unsigned,
    autoincrement: true,
    primarykey: true
  },
}, { sequelize });
const idattributemeta = getattributemetadata(user, 'id'); // works!
// @ts-expect-error
const nameattributemeta = getattributemetadata(user, 'name'); // fails because 'name' is not an attribute of user

查看笔记

扫码一下
查看教程更方便
网站地图