通过类型重载为egg-mongoose提供具体类型支持

1,355 阅读2分钟

为什么egg-mongoose不能提供类型支持

我们查看egg- mongoose对egg的扩展,进入/node_modules/egg-mongoose/index.d.ts,我们看到egg-mongoose对egg进行了两个扩展

import * as mongoose from 'mongoose';

declare module 'egg' {

  type MongooseModels = {
    [key: string]: mongoose.Model<any>
  };

  type MongooseSingleton = {
    clients: Map<string, mongoose.Connection>,
    get (id: string) : mongoose.Connection
  };

  type MongooseConfig = {
    url: string,
    options?: mongoose.ConnectionOptions
  };

  // extend app
  interface Application {
    mongooseDB: mongoose.Connection | MongooseSingleton;
    mongoose: typeof mongoose;
    model: MongooseModels;
  }

  // extend context
  interface Context {
    model: MongooseModels;
  }

  // extend your config
  interface EggAppConfig {
    mongoose: {
      url?: string,
      options?: mongoose.ConnectionOptions,
      client?: MongooseConfig,
      clients?: {
        [key: string]: MongooseConfig
      }
    };
  }

}

分别在20行(Application)和27行(Context),两个interface都扩展了model属性,类型为MongooseModels。而在第5行(type MongooseModels),我们看到MongooseModels是一个key:value形式的对象,key和value都有不确定性,这是导致ctx.model无法类型推断的原因。

如何扩展eggjs的类型定义

egg为我们提供了一个目录,该目录为 TS 的规范,在里面的 **/*.d.ts 文件将被自动识别。

  • 开发者需要手写的建议放在 typings/index.d.ts 中。
  • 工具会自动生成 typings/{app,config}/**.d.ts ,请勿自行修改,避免被覆盖。

需要处理的问题

type MongooseModels = {
  [key: string]: mongoose.Model<any>
};

我们需要将MongooseModels进行修改

  1. 将model的名称对应为[key]
  2. 将mongoose.Model中的any替换为model中定义的props,也就是将T=any变得明确
import 'egg';
import {Connection,Model} from 'mongoose'
//定义的model属性
import {UserProps} from '../app/model/user'

declare module 'egg' {
    interface MongooseModels{
        User:Model<UserProps>
    }
}

如何自动生成

egg默认使用了egg-ts-helper为我们做了如下支持

// This file is created by egg-ts-helper@1.33.0
// Do not modify this file!!!!!!!!!

import 'egg';
import ExportUser from '../../../app/model/user';

declare module 'egg' {
  interface IModel {
    User: ReturnType<typeof ExportUser>;
  }
}

egg-ts-helper默认导入了model下的所有文件,并将其类型重载到IModel上,我们可以通过将MongooseModels类型扩展为IModel达到自动创建类型支持

import 'egg';
import { Model } from 'mongoose'

declare module 'egg' {
    interface MongooseModels extends IModel {
        [key: string]: Model<any>
    }
}

总结

我们最开始通过手动书写MongooseModels的属性来达到类型支持的目的,然而这种方式过于繁琐,需要我们手动去导入各个model。我们发现egg-ts-helper已经为我们自动导入了所有的model,此时可以通过将MongooseModels extends IModel,从而为egg-mongoose本身添加明确的类型定义。