不要怂,就是干,撸起袖子干!

Commit 5cabcbc8 by Constantin Metz Committed by GitHub

feat(types): Add ModelDefined type as syntactic sugar (#12445)

1 parent 4d82907d
...@@ -15,12 +15,13 @@ In order to avoid installation bloat for non TS users, you must install the foll ...@@ -15,12 +15,13 @@ In order to avoid installation bloat for non TS users, you must install the foll
Example of a minimal TypeScript project with strict type-checking for attributes. Example of a minimal TypeScript project with strict type-checking for attributes.
**NOTE:** Keep the following code in sync with `typescriptDocs/ModelInit.ts` to ensure it typechecks correctly. **NOTE:** Keep the following code in sync with `/types/test/typescriptDocs/ModelInit.ts` to ensure it typechecks correctly.
```ts ```ts
import { import {
Sequelize, Sequelize,
Model, Model,
ModelDefined,
DataTypes, DataTypes,
HasManyGetAssociationsMixin, HasManyGetAssociationsMixin,
HasManyAddAssociationMixin, HasManyAddAssociationMixin,
...@@ -104,6 +105,16 @@ class Address extends Model<AddressAttributes> implements AddressAttributes { ...@@ -104,6 +105,16 @@ class Address extends Model<AddressAttributes> implements AddressAttributes {
public readonly updatedAt!: Date; public readonly updatedAt!: Date;
} }
// 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
interface NoteCreationAttributes extends Optional<NoteAttributes, 'id' | 'title'> {};
Project.init( Project.init(
{ {
id: { id: {
...@@ -164,6 +175,32 @@ Address.init( ...@@ -164,6 +175,32 @@ Address.init(
} }
); );
// 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. // Here we associate which actually populates out pre-declared `association` static and other methods.
User.hasMany(Project, { User.hasMany(Project, {
sourceKey: "id", sourceKey: "id",
......
...@@ -2849,6 +2849,8 @@ export type ModelType = typeof Model; ...@@ -2849,6 +2849,8 @@ export type ModelType = typeof Model;
// must come first for unknown reasons. // must come first for unknown reasons.
export type ModelCtor<M extends Model> = typeof Model & { new(): M }; export type ModelCtor<M extends Model> = typeof Model & { new(): M };
export type ModelDefined<S, T> = ModelCtor<Model<S, T>>;
export type ModelStatic<M extends Model> = { new(): M }; export type ModelStatic<M extends Model> = { new(): M };
export default Model; export default Model;
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import { import {
Sequelize, Sequelize,
Model, Model,
ModelDefined,
DataTypes, DataTypes,
HasManyGetAssociationsMixin, HasManyGetAssociationsMixin,
HasManyAddAssociationMixin, HasManyAddAssociationMixin,
...@@ -12,9 +13,9 @@ import { ...@@ -12,9 +13,9 @@ import {
HasManyCountAssociationsMixin, HasManyCountAssociationsMixin,
HasManyCreateAssociationMixin, HasManyCreateAssociationMixin,
Optional, Optional,
} from 'sequelize'; } from "sequelize";
const sequelize = new Sequelize('mysql://root:asd123@localhost:3306/mydb'); const sequelize = new Sequelize("mysql://root:asd123@localhost:3306/mydb");
// These are all the attributes in the User model // These are all the attributes in the User model
interface UserAttributes { interface UserAttributes {
...@@ -24,7 +25,7 @@ interface UserAttributes { ...@@ -24,7 +25,7 @@ interface UserAttributes {
} }
// Some attributes are optional in `User.build` and `User.create` calls // Some attributes are optional in `User.build` and `User.create` calls
interface UserCreationAttributes extends Optional<UserAttributes, 'id'> {} interface UserCreationAttributes extends Optional<UserAttributes, "id"> {}
class User extends Model<UserAttributes, UserCreationAttributes> class User extends Model<UserAttributes, UserCreationAttributes>
implements UserAttributes { implements UserAttributes {
...@@ -60,7 +61,7 @@ interface ProjectAttributes { ...@@ -60,7 +61,7 @@ interface ProjectAttributes {
name: string; name: string;
} }
interface ProjectCreationAttributes extends Optional<ProjectAttributes, 'id'> {} interface ProjectCreationAttributes extends Optional<ProjectAttributes, "id"> {}
class Project extends Model<ProjectAttributes, ProjectCreationAttributes> class Project extends Model<ProjectAttributes, ProjectCreationAttributes>
implements ProjectAttributes { implements ProjectAttributes {
...@@ -87,6 +88,17 @@ class Address extends Model<AddressAttributes> implements AddressAttributes { ...@@ -87,6 +88,17 @@ class Address extends Model<AddressAttributes> implements AddressAttributes {
public readonly updatedAt!: Date; public readonly updatedAt!: Date;
} }
// 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
interface NoteCreationAttributes
extends Optional<NoteAttributes, "id" | "title"> {}
Project.init( Project.init(
{ {
id: { id: {
...@@ -105,8 +117,8 @@ Project.init( ...@@ -105,8 +117,8 @@ Project.init(
}, },
{ {
sequelize, sequelize,
tableName: 'projects', tableName: "projects",
}, }
); );
User.init( User.init(
...@@ -126,9 +138,9 @@ User.init( ...@@ -126,9 +138,9 @@ User.init(
}, },
}, },
{ {
tableName: 'users', tableName: "users",
sequelize, // passing the `sequelize` instance is required sequelize, // passing the `sequelize` instance is required
}, }
); );
Address.init( Address.init(
...@@ -142,30 +154,56 @@ Address.init( ...@@ -142,30 +154,56 @@ Address.init(
}, },
}, },
{ {
tableName: 'address', tableName: "address",
sequelize, // passing the `sequelize` instance is required sequelize, // passing the `sequelize` instance is required
}
);
// 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. // Here we associate which actually populates out pre-declared `association` static and other methods.
User.hasMany(Project, { User.hasMany(Project, {
sourceKey: 'id', sourceKey: "id",
foreignKey: 'ownerId', foreignKey: "ownerId",
as: 'projects', // this determines the name in `associations`! as: "projects", // this determines the name in `associations`!
}); });
Address.belongsTo(User, { targetKey: 'id' }); Address.belongsTo(User, { targetKey: "id" });
User.hasOne(Address, { sourceKey: 'id' }); User.hasOne(Address, { sourceKey: "id" });
async function doStuffWithUser() { async function doStuffWithUser() {
const newUser = await User.create({ const newUser = await User.create({
name: 'Johnny', name: "Johnny",
preferredName: 'John', preferredName: "John",
}); });
console.log(newUser.id, newUser.name, newUser.preferredName); console.log(newUser.id, newUser.name, newUser.preferredName);
const project = await newUser.createProject({ const project = await newUser.createProject({
name: 'first!', name: "first!",
}); });
const ourUser = await User.findByPk(1, { const ourUser = await User.findByPk(1, {
...@@ -176,4 +214,4 @@ async function doStuffWithUser() { ...@@ -176,4 +214,4 @@ async function doStuffWithUser() {
// Note the `!` null assertion since TS can't know if we included // Note the `!` null assertion since TS can't know if we included
// the model or not // the model or not
console.log(ourUser.projects![0].name); console.log(ourUser.projects![0].name);
} }
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!