[javascript] Typescript 방식의 몽구스…?

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


답변

necroposting에 대해 죄송하지만 누군가에게는 여전히 흥미로울 수 있습니다. 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);


답변

여기에서 대부분의 답변은 TypeScript 클래스 / 인터페이스 및 몽구스 스키마의 필드를 반복합니다. 단일 소스 소스가 없다는 것은 프로젝트가 더 복잡해지고 더 많은 개발자가 작업하기 때문에 유지 관리 위험을 나타냅니다. 필드가 동기화되지 않을 가능성이 더 큽니다 . 이것은 클래스가 몽구스 스키마와 다른 파일에있을 때 특히 나쁩니다.

필드를 동기화 상태로 유지하려면 필드를 한 번 정의하는 것이 좋습니다. 이를 수행하는 몇 가지 라이브러리가 있습니다.

나는 아직 그들 중 누구에게도 완전히 확신하지 못했지만 typegoose는 적극적으로 유지되는 것처럼 보이며 개발자는 내 PR을 수락했습니다.

한발 앞서 생각하기 : GraphQL 스키마를 믹스에 추가하면 다른 계층의 모델 복제가 나타납니다. 이 문제를 극복하는 한 가지 방법 은 GraphQL 스키마에서 TypeScript 및 몽구스 코드생성 하는 것입니다.


답변

다음은 몽구스 스키마와 일반 모델을 일치시키는 강력한 유형의 방법입니다. 컴파일러는 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]>
};

user.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);


답변

다른 방법을 추가하십시오 ( @types/mongoose와 함께 설치해야 함 npm install --save-dev @types/mongoose).

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,
    // ...
}));

그리고 사이의 차이 interfacetype, 읽어 보시기 바랍니다 이 답변

이 방법은 장점이 있습니다. Mongoose 정적 메서드 입력을 추가 할 수 있습니다.

interface IUserModel extends IUser, mongoose.Document {
  generateJwt: () => string
}