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
提供了新的实用程序类型, 将大大减少数量 必需的样板: inferattributes
和 infercreationattributes
。 他们将提取属性类型 直接来自模型:
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...
}
关于 inferattributes
和 infercreationattributes
工作需要了解的重要事项: 它们将选择类的所有声明属性,除了:
- 静态字段和方法。
- 方法(任何类型为函数的东西)。
- 类型使用铭记类型 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);
获取模型的属性
如果您需要访问给定模型的属性列表, 你需要使用 attributes
和 creationattributes
。
它们将返回作为参数传递的模型的属性(和创建属性)。
不要将它们与 inferattributes
和 infercreationattributes
混淆。 这两种实用程序类型应该只被使用 在模型的定义中自动从模型的公共类字段创建属性列表。 它们仅适用于基于类的模型定义(使用 model.init
时)。
attributes
和 creationattributes
将返回任何模型的属性列表, 无论它们是如何创建的(无论是 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