定义一个模型

我们要定义一个模型,首先我们会用到define, 它的返回值类似是 Model<TInstance, TAttributes>

  1. define<TInstance, TAttributes>(modelName: string, attributes: DefineAttributes,
  2. options?: DefineOptions<TInstance>): Model<TInstance, TAttributes>;

在 defined 方法中, <TInstance, TAttributes> 这俩泛型会传给 Model。泛型是用来解决描述某一类型,且在定义之前,无法明确知道某一变量的类型的问题。换句话说,就是函数的返回值,要依赖你传递进来的对象的类型。函数的括号 () 传递的是参数,泛型的 <> 则是传递描述类型。接下来我们继续看 Model 的定义。

  1. interface Model<TInstance, TAttributes> extends Hooks<TInstance>, Associations {}

接口是支持继承的,接口是描述一个对象的属性与方法,并且进行约束。此时 Model 具有 Hooks,Associations 的描述。Associations 里面包含定义模型直接关系的一些方法,Hooks 是定义模型生命周期的一些方法(删除数据之前,删除方法之后,创建方法之后….等等)。然后我们在 Model 方法里面,我们可以看到很多返回值为TInstance 的类型的函数。

比如

  1. build(record?: TAttributes, options?: BuildOptions): TInstance;

build 是创建一个实例的方法,它会返回 TInstance ,也就是说这个 TInstance 这个类型 (interface) 要我们自己来写。

1.用 interface 声明一下模型有哪些属性,且是何种类型

  1. interface UserAttributes {
  2. email: string;
  3. name: string;
  4. }

2.声明一个描述模型的实例变量的类型,表明可以访问的属性和方法

  1. interface UserInstance extends Sequelize.Instance<UserAttributes> {
  2. id: number;
  3. createdAt: Date;
  4. updatedAt: Date;
  5. email: string;
  6. name: string;
  7. }

FAQ: 为什么要继承 Sequelize.Instance<UserAttributes> 呢?Sequelize.Instance 里面定义了一些所有模型实例通用的方法,而且我们还需要把 UserAttributes (我们自定义的属性) 给传递进去。

FAQ: 你为什么知道需要继承 Sequelize.Instance因为我发现不继承的话,通过build能访问的方法里面根本就没有官方 API 里面所说的那些方法。这里我说的没有方法是指 TypeScript 这一个层面没有,假如通过(use as any)去调用,还是可以调用得到的,只是 TypeScript 这一层没有代码提示了而已。当我遇到这个问题之后,我就想看看老外是如何写的,所以我就直接通过百度typescript sequelize 就搜索得到了,之后我又进入 d.ts 文件里面去看了一下 Instance 这个接口。

这里我还遇见了一个问题那就这个模型没法使用 new 关键字,因为没有constructor构造器,也没有 prototype,当然这里说的没有,也是在 typescript 这个层面。对于一切报类型错误的大家都可以用 变量 as any,来解决问题,也就是类型断言,断言为兼容所有类型的类型即可,也可以说是万能类型。

我们还可以深入看一下 Instance 接口,在 2724 - 2922 行,这里面都是它可以访问的方法。

3.定义模型

  1. const S = Sequelize
  2. const User = sequelize.define<UserInstance, UserAttributes>
  3. ('User', {
  4. email: S.STRING,
  5. name: S.STRING
  6. });

之前我们定义的属性,idcreatedAtupdateAt是 sequelize 默认生成的,当然也可以通过配置项关闭自动时间戳(创建的时间,修改的时间),或者自己定义 id 的类型与约束,sequelize 会有限以你的配置为标准。像上面我们就没有传递 id 的类型与约束,sequelize 就给我们自动补上了,因为一个表是不可能没有主键的。

4.给模型定义一些方法

  1. (User as any).prototype.say = function(this: UserInstance) {
  2. console.log('name ' + this.name);
  3. }

Tip:有3点,我们需要注意一下。

  • (User as any) 避免报错
  • 使用的是 function(){} 来保存 this 指向,假如使用箭头函数,则 this 为空。
  • this: UserInstance 在第一个参数的位置,可以指定 this 的类型,这样 this 就有的代码提示。

5.运行

  1. async function main() {
  2. try {
  3. await User.sync();
  4. const user = User.build({ name: 'yugo', email: 'soome@gmail.com' });
  5. await user.save();
  6. // -----------------------
  7. // const user = await User.findAll();
  8. // (user[0] as any).say()
  9. process.exit(0)
  10. }catch(e){
  11. console.log(e)
  12. }
  13. }
  14. main()

await 后面跟一个 Promise 对象,像syncsave返回值其实都是一个 Promise 对象。sync将我们 User 的模型同步到数据库表格,build会创建一个模型实例,但并不会存储,需要自行调用save方法,或者调用create方法,它会直接存储。之后我们通过ts-node index.ts进行运行,当有数据了之后,我们再解开后俩句的注释,注释前三句,再次运行测试一下查询是否正常。