NestJS の MongoDB 利用方法マニュアルにある通り、こんなスキーマがあったとして、
// cats.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';
@Schema()
export class Cat {
@Prop({
type: String,
required: true,
})
name: string;
@Prop({
type: Number,
required: true,
})
age: number;
}
export type CatDocument = HydratedDocument<Cat>;
export const CatSchema = SchemaFactory.createForClass(Cat);
インスタンスを生成しようとする。
// cats.repository.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Cat, PreRegisterCat } from './cat.entity';
import { CatDocument } from './cats.schema';
@Injectable()
export class CatsRepository {
constructor(
@InjectModel(Cat.name)
private readonly CatModel: Model<CatDocument>,
) {}
async create() {
const cat = new this.CatModel({
name: 'nyan',
age: 10,
});
await cat.save();
}
}
と、 const cat = new this.CatModel({ name: 'nyan', age: 10 });
の部分で適当な値を代入しても、 TypeScript でのチェックは効かない。もちろん、必須項目が未設定であれば、後続の cat.save()
時にランタイムエラーとして Validation Error
が Throw される。( cat.validate()
で事前チェックも可能 )
new するときの型チェックを有効にする方法
InferSchemaType
と Model
への generics で対応できた。
// cats.schema.ts
// 略
- import { HydratedDocument } from 'mongoose';
+ import { HydratedDocument, InferSchemaType } from 'mongoose';
// 略
export const CatSchema = SchemaFactory.createForClass(Cat);
+ export type CatPayload = InferSchemaType<typeof CatSchema>;
// => {
// name: string;
// age: number;
// }
として、 repository では
- import { CatDocument } from './cats.schema';
+ import { CatDocument, CatPayload } from './cats.schema';
// 略
async create() {
- const cat = new this.CatModel({
+ const cat = new this.CatModel<CatPayload>({
name: 'nyan',
age: 10,
});
await cat.save();
}
// 略
という形で指定すれば良い。
存在しないフィールドを指定すると ts でエラーになる。
const cat = new this.CatModel<CatPayload>({ name: 'nyan', age: 10, microchipping: false });
// ~~~~~~~~~~~~~~~~~~~~
// Argument of type '{ name: string; age: number; microchipping: boolean; }' is not assignable to parameter of type 'Cat'.
// Object literal may only specify known properties, and 'microchipping' does not exist in type 'Cat'
注意点として、あくまで Type Check しているだけなので、 MongoDB 側で Schema 定義と異なる制約が設定されていたら TypeCheck 上エラーにはならないが、 save()
時にランタイムエラーが発生する (当たり前だしこれは RDB でも一緒) 。
Mongoose のインスタンス生成時にエラーを発生させるのではなく、データの永続化のタイミングでランタイムエラーを発生させるという挙動自体は JavaScript や Ruby のようなインタプリタ言語であれば扱いやすいので、このような仕様になるのは仕方がない。まずからのインスタンスを作ってからちょっとずつフィールドを足していって最終的に valid な状態になったら save したほうが良いケースもあるので、Type Check させたいかどうかはユースケース次第。
他に良い方法があれば教えて下さい。