TypeScriptでMongooseモデルを実装しようとしています。 Googleを精査した結果、ハイブリッドアプローチ(JSとTSの組み合わせ)のみが明らかになりました。私のやや素朴なアプローチで、JSなしでUserクラスを実装するにはどうすればよいですか?
手荷物なしでIUserModelができるようにしたい。
import {IUser} from './user.ts';
import {Document, Schema, Model} from 'mongoose';
// mixing in a couple of interfaces
interface IUserDocument extends IUser, Document {}
// mongoose, why oh why '[String]'
// TODO: investigate out why mongoose needs its own data types
let userSchema: Schema = new Schema({
userName : String,
password : String,
firstName : String,
lastName : String,
email : String,
activated : Boolean,
roles : [String]
});
// interface we want to code to?
export interface IUserModel extends Model<IUserDocument> {/* any custom methods here */}
// stumped here
export class User {
constructor() {}
}
以下にその方法を示します。
export interface IUser extends mongoose.Document {
name: string;
somethingElse?: number;
};
export const UserSchema = new mongoose.Schema({
name: {type:String, required: true},
somethingElse: Number,
});
const User = mongoose.model<IUser>('User', UserSchema);
export default User;
型定義とデータベース実装を切り離したい場合の別の選択肢。
import {IUser} from './user.ts';
import * as mongoose from 'mongoose';
type UserType = IUser & mongoose.Document;
const User = mongoose.model<UserType>('User', new mongoose.Schema({
userName : String,
password : String,
/* etc */
}));
ここからのインスピレーション: https://github.com/Appsilon/styleguide/wiki/mongoose-TypeScript-models
ネクロポストで申し訳ありませんが、これはまだ誰かにとって興味深いものです。 Typegoose はモデルを定義するためのよりモダンでエレガントな方法を提供すると思います
ドキュメントの例を次に示します。
import { prop, Typegoose, ModelType, InstanceType } from 'typegoose';
import * as mongoose from 'mongoose';
mongoose.connect('mongodb://localhost:27017/test');
class User extends Typegoose {
@prop()
name?: string;
}
const UserModel = new User().getModelForClass(User);
// UserModel is a regular Mongoose Model with correct types
(async () => {
const u = new UserModel({ name: 'JohnDoe' });
await u.save();
const user = await UserModel.findOne();
// prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
console.log(user);
})();
既存の接続シナリオの場合、次のように使用できます(実際の状況ではより可能性が高く、ドキュメントで明らかにされている可能性があります)。
import { prop, Typegoose, ModelType, InstanceType } from 'typegoose';
import * as mongoose from 'mongoose';
const conn = mongoose.createConnection('mongodb://localhost:27017/test');
class User extends Typegoose {
@prop()
name?: string;
}
// Notice that the collection name will be 'users':
const UserModel = new User().getModelForClass(User, {existingConnection: conn});
// UserModel is a regular Mongoose Model with correct types
(async () => {
const u = new UserModel({ name: 'JohnDoe' });
await u.save();
const user = await UserModel.findOne();
// prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
console.log(user);
})();
ts-mongoose
を試してください。条件付きタイプを使用してマッピングを行います。
import { createSchema, Type, typedModel } from 'ts-mongoose';
const UserSchema = createSchema({
username: Type.string(),
email: Type.string(),
});
const User = typedModel('User', UserSchema);
別の方法を追加するだけです:
import { IUser } from './user.ts';
import * as mongoose from 'mongoose';
interface IUserModel extends IUser, mongoose.Document {}
const User = mongoose.model<IUserModel>('User', new mongoose.Schema({
userName: String,
password: String,
// ...
}));
interface
とtype
の違いは、 この回答 を読んでください
この方法には利点があり、Mongooseの静的メソッドの型付けを追加できます。
interface IUserModel extends IUser, mongoose.Document {
generateJwt: () => string
}
@types/mongoose
をインストールした場合
npm install --save-dev @types/mongoose
あなたはそうすることができます
import {IUser} from './user.ts';
import { Document, Schema, model} from 'mongoose';
type UserType = IUser & Document;
const User = model<UserType>('User', new Schema({
userName : String,
password : String,
/* etc */
}));
PS:@Hongbo Miao
の回答をコピーしました
以下は、単純なモデルをマングーススキーマと一致させるための強力な型指定された方法です。コンパイラは、mongoose.Schemaに渡された定義がインターフェイスと一致することを確認します。スキーマを取得したら、使用できます
common.ts
export type IsRequired<T> =
undefined extends T
? false
: true;
export type FieldType<T> =
T extends number ? typeof Number :
T extends string ? typeof String :
Object;
export type Field<T> = {
type: FieldType<T>,
required: IsRequired<T>,
enum?: Array<T>
};
export type ModelDefinition<M> = {
[P in keyof M]-?:
M[P] extends Array<infer U> ? Array<Field<U>> :
Field<M[P]>
};
ser.ts
import * as mongoose from 'mongoose';
import { ModelDefinition } from "./common";
interface User {
userName : string,
password : string,
firstName : string,
lastName : string,
email : string,
activated : boolean,
roles : Array<string>
}
// The typings above expect the more verbose type definitions,
// but this has the benefit of being able to match required
// and optional fields with the corresponding definition.
// TBD: There may be a way to support both types.
const definition: ModelDefinition<User> = {
userName : { type: String, required: true },
password : { type: String, required: true },
firstName : { type: String, required: true },
lastName : { type: String, required: true },
email : { type: String, required: true },
activated : { type: Boolean, required: true },
roles : [ { type: String, required: true } ]
};
const schema = new mongoose.Schema(
definition
);
スキーマを取得したら、次のような他の回答に記載されているメソッドを使用できます。
const userModel = mongoose.model<User & mongoose.Document>('User', schema);
// imports
import { ObjectID } from 'mongodb'
import { Document, model, Schema, SchemaDefinition } from 'mongoose'
import { authSchema, IAuthSchema } from './userAuth'
// the model
export interface IUser {
_id: ObjectID, // !WARNING: No default value in Schema
auth: IAuthSchema
}
// IUser will act like it is a Schema, it is more common to use this
// For example you can use this type at passport.serialize
export type IUserSchema = IUser & SchemaDefinition
// IUser will act like it is a Document
export type IUserDocument = IUser & Document
export const userSchema = new Schema<IUserSchema>({
auth: {
required: true,
type: authSchema,
}
})
export default model<IUserDocument>('user', userSchema)