Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

在 TypeScript 中定义 Mongoose 的 Schema、Model 与 Document #62

Open
lmk123 opened this issue Apr 11, 2018 · 0 comments
Open

在 TypeScript 中定义 Mongoose 的 Schema、Model 与 Document #62

lmk123 opened this issue Apr 11, 2018 · 0 comments

Comments

@lmk123
Copy link
Owner

lmk123 commented Apr 11, 2018

定义静态方法与实例方法

mongoose 可以在 Schema 上定义 Model 的静态方法与 Document 的实例方法,但 TypeScript 是无法识别到的。

例如下面的代码:

const { Schema, model } = require('mongoose')

const userSchema = new Schema({
  name: String,
  pwd: String
})

// 定义 Model 的静态方法
userSchema.statics.findUser = function(name, pwd) {
  return this.findOne({ name, pwd }).exec()
}

// 定义 Document 的实例方法
userSchema.methods.sayHi = function() {
  console.log(`My name is ${this.name} and my password is ${this.pwd}.`)
}

// 现在就可以使用上面定义的两个方法了
const UserModel = model('User', userSchema)
// 使用静态方法
UserModel.findUser('limingkai', '123').then(...)

const newUser = new UserModel({
  name: 'hh',
  pwd: '456'
})
// 使用实例方法
newUser.sayHi()

我在 Schema 上定义了一个静态方法和一个实例方法,但如果用 TypeScript 写上面的代码,在调用这两个方法的时候 VS Code 会报错,提示我找不到这两个方法的类型定义。

为了让 TypeScript 能检测到这些方法,只能自己手动将这些方法的类型添加上去了,例如上面的代码在 TypeScript 中可以这样写:

import { Document, Schema, Model, model } from 'mongoose'

interface UserDocument extends Document {
  // 属性的结构等同于 Schema 中的定义
  name: string
  pwd: string
  // 实例方法在这里定义
  sayHi(this: UserDocument): void
}

// 定义 UserModel
interface UserModelConstructor extends Model<UserDocument> {
  // 静态方法在这里定义
  findUser(this: Model<UserDocument>): Promise<UserDocument>
}

const userSchema = new Schema({
  name: String,
  pwd: String
})

// 注册静态方法
userSchema.statics.findUser = function(name, pwd) {
  // 这里能识别 this 的类型
  return this.findOne({ name, pwd }).exec()
} as UserModelConstructor['findUser']

// 注册实例方法
userSchema.methods.sayHi = function() {
  // 这里也能识别 this 的类型
  console.log(`My name is ${this.name} and my password is ${this.pwd}.`)
} as UserDocument['sayHi']

const UserModel = model('User', userSchema) as UserModelConstructor
// 可以识别到 UserModel.findUser 了
UserModel.findUser(...)

const newUser = new UserModel({ name: 'hh', pwd: '456' })
// 也能识别到实例上的 sayHi() 方法了
newUser.sayHi()

定义非 Document 实例类型

mongoose 的 .lean() 方法返回的是一个不是 Document 实例的纯数据,为了与 Document 区分,一开始我的做法是定义两个结构差不多的类型,例如:

interface RawUser {
  _id: ObjectID
  name: string
  age: number
}

interface User extends Document {
  _id: ObjectID
  name: string
  age: number
}

上面的例子中,RawUserUser 之间唯一的不同就是 User 继承了 DocumentRawUser 没有,但维护这样两个 interface 很繁琐且容易出错,后来我找到了这样一种写法:

interface RawUser {
  _id: ObjectID
  name: string
  age: number
}

type RawUserDoc = RawUser & Document

interface UserDoc extends RawUserDoc {
  // Document 上的 _id 的类型是 any,
  // 这里为了覆盖它的类型所以用了一个新的 interface
  _id: ObjectID
}

社区提供的解决方案

最近发现社区里已经有人针对这个问题提供了一种解决方案:https://github.com/szokodiakos/typegoose

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant