如何在 Sequelize 中使用 TypeScript
How to use TypeScript with Sequelize
我已经使用 Fastify 用 Node、PostgreSQL、Sequelize 编写了我的服务器应用程序。
现在我想使用 TypeScript。谁能告诉我如何开始使用 TypeScript 重写我的服务器应用程序。
使用sequelize-typescript。将您的 table 和视图转换为扩展模型对象的 class。
使用 classes 中的注释来定义您的 table。
import {Table, Column, Model, HasMany} from 'sequelize-typescript';
@Table
class Person extends Model<Person> {
@Column
name: string;
@Column
birthday: Date;
@HasMany(() => Hobby)
hobbies: Hobby[];
}
通过创建对象创建到数据库的连接:
const sequelize = new Sequelize(configuration...).
然后将您的 table 注册到此对象。
sequelize.add([Person])
如需进一步参考,请查看此模块。
Sequelize-Typescript
已更新
/**
* Keep this file in sync with the code in the "Usage" section
* in /docs/manual/other-topics/typescript.md
*
* Don't include this comment in the md file.
*/
import {
Association, DataTypes, HasManyAddAssociationMixin, HasManyCountAssociationsMixin,
HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, HasManyHasAssociationMixin,
HasManySetAssociationsMixin, HasManyAddAssociationsMixin, HasManyHasAssociationsMixin,
HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin, Model, ModelDefined, Optional,
Sequelize, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, ForeignKey,
} 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>;
// foreign keys are automatically added by associations methods (like Project.belongsTo)
// by branding them using the `ForeignKey` type, `Project.init` will know it does not need to
// display an error if ownerId is missing.
declare ownerId: ForeignKey<User['id']>;
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: ForeignKey<User['id']>;
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
},
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(
{
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);
}
(async () => {
await sequelize.sync();
await doStuffWithUser();
})();
旧
您应该尽可能避免使用装饰器,它们不是 ECMAScript 标准。他们甚至考虑遗产。这就是为什么我要向您展示如何将 sequelize 与打字稿一起使用。
我们只需要按照文档:https://sequelize.org/v5/manual/typescript.html 但是因为它不是很清楚,或者至少对我来说是这样。我花了一段时间才明白。
那里说你需要安装这个树东西
* @types/node
* @types/validator // this one is not need it
* @types/bluebird
npm i -D @types/node @types/bluebird
那么让我们假设您的项目如下所示:
myProject
--src
----models
------index.ts
------user-model.ts
------other-model.ts
----controllers
----index.ts
--package.json
让我们先创建用户模型
`./src/models/user-model.ts`
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface UserAttributes {
id: number;
name: string;
email: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface UserModel extends Model<UserAttributes>, UserAttributes {}
export class User extends Model<UserModel, UserAttributes> {}
export type UserStatic = typeof Model & {
new (values?: object, options?: BuildOptions): UserModel;
};
export function UserFactory (sequelize: Sequelize): UserStatic {
return <UserStatic>sequelize.define("users", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
});
}
现在只是为了玩箭头让我们创建另一个-model.ts
`./src/models/another-model.ts`
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface SkillsAttributes {
id: number;
skill: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface SkillsModel extends Model<SkillsAttributes>, SkillsAttributes {}
export class Skills extends Model<SkillsModel, SkillsAttributes> {}
export type SkillsStatic = typeof Model & {
new (values?: object, options?: BuildOptions): SkillsModel;
};
export function SkillsFactory (sequelize: Sequelize): SkillsStatic {
return <SkillsStatic>sequelize.define("skills", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
skill: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
});
}
我们的实体已经完成。现在是数据库连接。
打开 ./src/models/index.ts
我们要放置 seqelize 实例的地方
`./src/models/index.ts`
import * as sequelize from "sequelize";
import {userFactory} from "./user-model";
import {skillsFactory} from "./other-model";
export const dbConfig = new sequelize.Sequelize(
(process.env.DB_NAME = "db-name"),
(process.env.DB_USER = "db-user"),
(process.env.DB_PASSWORD = "db-password"),
{
port: Number(process.env.DB_PORT) || 54320,
host: process.env.DB_HOST || "localhost",
dialect: "postgres",
pool: {
min: 0,
max: 5,
acquire: 30000,
idle: 10000,
},
}
);
// SOMETHING VERY IMPORTANT them Factory functions expect a
// sequelize instance as parameter give them `dbConfig`
export const User = userFactory(dbConfig);
export const Skills = skillsFactory(dbConfig);
// Users have skills then lets create that relationship
User.hasMay(Skills);
// or instead of that, maybe many users have many skills
Skills.belongsToMany(Users, { through: "users_have_skills" });
// the skill is the limit!
在我们的index.ts上添加,如果你只是想打开连接
db.sequelize
.authenticate()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
或者如果你想为它们创建表
db.sequelize
.sync()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
有些像这样
import * as bodyParser from "body-parser";
import * as express from "express";
import { dbConfig } from "./models";
import { routes } from "./routes";
import { logger } from "./utils/logger";
import { timeMiddleware } from "./utils/middlewares";
export function expressApp () {
dbConfig
.authenticate()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
const app: Application = express();
if (process.env.NODE_ENV === "production") {
app.use(require("helmet")());
app.use(require("compression")());
} else {
app.use(require("cors")());
}
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "5m" }));
app.use(timeMiddleware);
app.use("/", routes(db));
return app;
}
再一次,天空是极限。
如果你这样做,你将拥有自动完成的所有功能。
这里有一个例子:https://github.com/EnetoJara/resume-app
我已经使用 Fastify 用 Node、PostgreSQL、Sequelize 编写了我的服务器应用程序。
现在我想使用 TypeScript。谁能告诉我如何开始使用 TypeScript 重写我的服务器应用程序。
使用sequelize-typescript。将您的 table 和视图转换为扩展模型对象的 class。
使用 classes 中的注释来定义您的 table。
import {Table, Column, Model, HasMany} from 'sequelize-typescript';
@Table
class Person extends Model<Person> {
@Column
name: string;
@Column
birthday: Date;
@HasMany(() => Hobby)
hobbies: Hobby[];
}
通过创建对象创建到数据库的连接:
const sequelize = new Sequelize(configuration...).
然后将您的 table 注册到此对象。
sequelize.add([Person])
如需进一步参考,请查看此模块。 Sequelize-Typescript
已更新
/**
* Keep this file in sync with the code in the "Usage" section
* in /docs/manual/other-topics/typescript.md
*
* Don't include this comment in the md file.
*/
import {
Association, DataTypes, HasManyAddAssociationMixin, HasManyCountAssociationsMixin,
HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, HasManyHasAssociationMixin,
HasManySetAssociationsMixin, HasManyAddAssociationsMixin, HasManyHasAssociationsMixin,
HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin, Model, ModelDefined, Optional,
Sequelize, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, ForeignKey,
} 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>;
// foreign keys are automatically added by associations methods (like Project.belongsTo)
// by branding them using the `ForeignKey` type, `Project.init` will know it does not need to
// display an error if ownerId is missing.
declare ownerId: ForeignKey<User['id']>;
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: ForeignKey<User['id']>;
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
},
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(
{
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);
}
(async () => {
await sequelize.sync();
await doStuffWithUser();
})();
旧
您应该尽可能避免使用装饰器,它们不是 ECMAScript 标准。他们甚至考虑遗产。这就是为什么我要向您展示如何将 sequelize 与打字稿一起使用。
我们只需要按照文档:https://sequelize.org/v5/manual/typescript.html 但是因为它不是很清楚,或者至少对我来说是这样。我花了一段时间才明白。
那里说你需要安装这个树东西
* @types/node
* @types/validator // this one is not need it
* @types/bluebird
npm i -D @types/node @types/bluebird
那么让我们假设您的项目如下所示:
myProject
--src
----models
------index.ts
------user-model.ts
------other-model.ts
----controllers
----index.ts
--package.json
让我们先创建用户模型
`./src/models/user-model.ts`
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface UserAttributes {
id: number;
name: string;
email: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface UserModel extends Model<UserAttributes>, UserAttributes {}
export class User extends Model<UserModel, UserAttributes> {}
export type UserStatic = typeof Model & {
new (values?: object, options?: BuildOptions): UserModel;
};
export function UserFactory (sequelize: Sequelize): UserStatic {
return <UserStatic>sequelize.define("users", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
});
}
现在只是为了玩箭头让我们创建另一个-model.ts
`./src/models/another-model.ts`
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface SkillsAttributes {
id: number;
skill: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface SkillsModel extends Model<SkillsAttributes>, SkillsAttributes {}
export class Skills extends Model<SkillsModel, SkillsAttributes> {}
export type SkillsStatic = typeof Model & {
new (values?: object, options?: BuildOptions): SkillsModel;
};
export function SkillsFactory (sequelize: Sequelize): SkillsStatic {
return <SkillsStatic>sequelize.define("skills", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
skill: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
});
}
我们的实体已经完成。现在是数据库连接。
打开 ./src/models/index.ts
我们要放置 seqelize 实例的地方
`./src/models/index.ts`
import * as sequelize from "sequelize";
import {userFactory} from "./user-model";
import {skillsFactory} from "./other-model";
export const dbConfig = new sequelize.Sequelize(
(process.env.DB_NAME = "db-name"),
(process.env.DB_USER = "db-user"),
(process.env.DB_PASSWORD = "db-password"),
{
port: Number(process.env.DB_PORT) || 54320,
host: process.env.DB_HOST || "localhost",
dialect: "postgres",
pool: {
min: 0,
max: 5,
acquire: 30000,
idle: 10000,
},
}
);
// SOMETHING VERY IMPORTANT them Factory functions expect a
// sequelize instance as parameter give them `dbConfig`
export const User = userFactory(dbConfig);
export const Skills = skillsFactory(dbConfig);
// Users have skills then lets create that relationship
User.hasMay(Skills);
// or instead of that, maybe many users have many skills
Skills.belongsToMany(Users, { through: "users_have_skills" });
// the skill is the limit!
在我们的index.ts上添加,如果你只是想打开连接
db.sequelize
.authenticate()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
或者如果你想为它们创建表
db.sequelize
.sync()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
有些像这样
import * as bodyParser from "body-parser";
import * as express from "express";
import { dbConfig } from "./models";
import { routes } from "./routes";
import { logger } from "./utils/logger";
import { timeMiddleware } from "./utils/middlewares";
export function expressApp () {
dbConfig
.authenticate()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
const app: Application = express();
if (process.env.NODE_ENV === "production") {
app.use(require("helmet")());
app.use(require("compression")());
} else {
app.use(require("cors")());
}
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "5m" }));
app.use(timeMiddleware);
app.use("/", routes(db));
return app;
}
再一次,天空是极限。 如果你这样做,你将拥有自动完成的所有功能。 这里有一个例子:https://github.com/EnetoJara/resume-app