ts-mongoose
Advanced tools
Comparing version 0.0.18 to 0.0.19-beta.0
import { SchemaOptions, Schema } from 'mongoose'; | ||
import { ConvertObject, TypeWithTimestamps } from './types'; | ||
declare type SchemaOpts = SchemaOptions & { | ||
statics?: { | ||
[x: string]: any; | ||
}; | ||
}; | ||
declare type CreateSchema = <T extends { | ||
[x: string]: any; | ||
}, O extends SchemaOptions>(definition?: T, options?: O extends SchemaOptions ? SchemaOptions : O) => Schema & { | ||
}, O extends SchemaOpts>(definition?: T, options?: O) => Schema & { | ||
definition: ConvertObject<TypeWithTimestamps<O, T>>; | ||
@@ -7,0 +12,0 @@ options: O; |
"use strict"; | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var mongoose_1 = require("mongoose"); | ||
exports.createSchema = function (definition, options) { | ||
return new mongoose_1.Schema(definition, options); | ||
if (!options) | ||
return new mongoose_1.Schema(definition, options); | ||
var statics = options.statics, opts = __rest(options, ["statics"]); | ||
var schema = new mongoose_1.Schema(definition, opts); | ||
if (statics) | ||
schema.statics = statics; | ||
return schema; | ||
}; |
import { SchemaOptions, Schema } from 'mongoose'; | ||
import { ConvertObject, TypeWithTimestamps } from './types'; | ||
declare type SchemaOpts = SchemaOptions & { | ||
statics?: { | ||
[x: string]: any; | ||
}; | ||
}; | ||
declare type CreateSchema = <T extends { | ||
[x: string]: any; | ||
}, O extends SchemaOptions>(definition?: T, options?: O extends SchemaOptions ? SchemaOptions : O) => Schema & { | ||
}, O extends SchemaOpts>(definition?: T, options?: O) => Schema & { | ||
definition: ConvertObject<TypeWithTimestamps<O, T>>; | ||
@@ -7,0 +12,0 @@ options: O; |
@@ -0,4 +1,21 @@ | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
}; | ||
import { Schema } from 'mongoose'; | ||
export var createSchema = function (definition, options) { | ||
return new Schema(definition, options); | ||
if (!options) | ||
return new Schema(definition, options); | ||
var statics = options.statics, opts = __rest(options, ["statics"]); | ||
var schema = new Schema(definition, opts); | ||
if (statics) | ||
schema.statics = statics; | ||
return schema; | ||
}; |
import { Schema, Document, Model } from 'mongoose'; | ||
import { Extract } from './types'; | ||
export declare function typedModel<T extends Schema>(name: string, schema?: T, collection?: string, skipInit?: boolean): Model<Document & Extract<T>>; | ||
import { Extract, ExtractStatics } from './types'; | ||
export declare function typedModel<T extends Schema>(name: string, schema?: T, collection?: string, skipInit?: boolean): ExtractStatics<T, Model<Document & Extract<T>>, Document & Extract<T>>; |
@@ -82,2 +82,13 @@ import { Types, Document } from 'mongoose'; | ||
export declare type TypeWithTimestamps<Opts, T> = Opts extends (TimestampsPresent | TimestampsEachPresent) ? T & CreatedAtType & UpdatedAtType : Opts extends TimestampCreatedByPresent ? T & CreatedAtType : Opts extends TimestampUpdatedByPresent ? T & UpdatedAtType : T; | ||
export declare type ModelInstanceType = 'ModelInstanceType'; | ||
export declare type ModelInstancesType = 'ModelInstancesType'; | ||
declare type ArgumentTypes<T> = T extends (...args: infer U) => infer R ? U : never; | ||
declare type ReplaceReturnType<T, TNewReturn> = (...a: ArgumentTypes<T>) => TNewReturn; | ||
export declare type ExtractStatics<T, Rest, I> = T extends { | ||
options: infer O; | ||
} ? O extends { | ||
statics: infer S; | ||
} ? { | ||
[P in keyof S]: S[P] extends (...args: any) => any ? ReturnType<S[P]> extends ModelInstanceType ? ReplaceReturnType<S[P], Promise<null | I>> : ReturnType<S[P]> extends ModelInstancesType ? ReplaceReturnType<S[P], Promise<I[]>> : S[P] : S[P]; | ||
} & Rest : Rest : Rest; | ||
export {}; |
{ | ||
"name": "ts-mongoose", | ||
"version": "0.0.18", | ||
"version": "0.0.19-beta.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
192
README.md
@@ -15,7 +15,8 @@ # ts-mongoose | ||
## The Problem | ||
When using mongoose and Typescript, you must define schemas and interfaces. Both definitions must be maintained separately and must match each other. It can be error-prone during development and cause overhead. | ||
When using mongoose and Typescript, you must define schemas and interfaces. Both definitions must be maintained separately and must match each other. It can be error-prone during development and cause overhead. | ||
`ts-mongoose` is a very lightweight library that allows you to create a mongoose schema and a typescript type from a common definition. | ||
All types as created from 1-liner functions and does not depend on decorators❗️. | ||
For example: | ||
@@ -31,51 +32,57 @@ `Type.string()` returns `{type: String, required: true}`, which is the same definition required in the original mongoose library. | ||
const AddressSchema = new Schema({ | ||
city: { type: String, required: true }, | ||
country: String, | ||
zip: String, | ||
}, { _id: false, timestamps: true }); | ||
const AddressSchema = new Schema( | ||
{ | ||
city: { type: String, required: true }, | ||
country: String, | ||
zip: String, | ||
}, | ||
{ _id: false, timestamps: true } | ||
); | ||
const PhoneSchema = new Schema({ | ||
phoneNumber: { type: Schema.Types.Number, required: true }, | ||
name: String | ||
}) | ||
name: String, | ||
}); | ||
const UserSchema = new Schema({ | ||
title: { type: String, required: true }, | ||
author: { type: String, required: true }, | ||
body: { type: String, required: true }, | ||
comments: [ | ||
{ | ||
body: { type: String, required: true }, | ||
date: { type: Date, required: true }, | ||
const UserSchema = new Schema( | ||
{ | ||
title: { type: String, required: true }, | ||
author: { type: String, required: true }, | ||
body: { type: String, required: true }, | ||
comments: [ | ||
{ | ||
body: { type: String, required: true }, | ||
date: { type: Date, required: true }, | ||
}, | ||
], | ||
date: { type: Date, default: Date.now, required: true }, | ||
hidden: { type: Boolean, required: true }, | ||
meta: { | ||
votes: { type: Schema.Types.Number }, | ||
favs: { type: Schema.Types.Number }, | ||
}, | ||
], | ||
date: { type: Date, default: Date.now, required: true }, | ||
hidden: { type: Boolean, required: true }, | ||
meta: { | ||
votes: { type: Schema.Types.Number }, | ||
favs: { type: Schema.Types.Number }, | ||
m: { | ||
type: Schema.Types.Mixed, | ||
required: true, | ||
}, | ||
gender: { | ||
type: Schema.Types.String, | ||
required: true, | ||
enum: ['male', 'female'], | ||
}, | ||
otherId: { | ||
type: Schema.Types.ObjectId, | ||
required: true, | ||
}, | ||
address: { | ||
type: AddressSchema, | ||
required: true, | ||
}, | ||
phones: { | ||
type: [PhoneSchema], | ||
required: true, | ||
}, | ||
}, | ||
m: { | ||
type: Schema.Types.Mixed, | ||
required: true, | ||
}, | ||
gender: { | ||
type: Schema.Types.String, | ||
required: true, | ||
enum: ['male', 'female'] | ||
}, | ||
otherId: { | ||
type: Schema.Types.ObjectId, | ||
required: true, | ||
}, | ||
address: { | ||
type: AddressSchema, | ||
required: true, | ||
}, | ||
phones: { | ||
type: [PhoneSchema], | ||
required: true, | ||
}, | ||
}, { timestamps: { createdAt: true } }); | ||
{ timestamps: { createdAt: true } } | ||
); | ||
@@ -90,3 +97,2 @@ interface UserProps extends Document { | ||
const User: Model<UserProps> = model('User', UserSchema); | ||
``` | ||
@@ -141,5 +147,6 @@ | ||
### API | ||
### API | ||
- Each type has two forms: required and optional | ||
```ts | ||
@@ -153,10 +160,14 @@ { | ||
``` | ||
- Each type accepts the same options from mongoose | ||
```ts | ||
{ | ||
// same as {type: String, required: true, unique: true, index: true} | ||
email: Type.string({unique: true, index: true}) | ||
email: Type.string({ unique: true, index: true }); | ||
} | ||
``` | ||
- Note that enum values need to be readonly array to be treated as literals by typescript | ||
```ts | ||
@@ -166,31 +177,45 @@ const genders = ['male', 'female'] as const; | ||
// same as {type: String, required: true, enum: ['male', 'female']} | ||
gender: Type.string({enum: genders}) | ||
gender: Type.string({ enum: genders }); | ||
} | ||
``` | ||
- `schema`, `object`, `array` types have a method `of` where you must provide a child type | ||
```ts | ||
{ | ||
// same as {type: [String], required: true} | ||
tags: Type.array().of(Type.string()) | ||
tags: Type.array().of(Type.string()); | ||
} | ||
``` | ||
- `schema.of(ExampleSchema)` has typical for Subdocument additional fields and methods. Setting `{ _id: false }` in SchemaOptions won't attach `_id` property in Subdocument | ||
```ts | ||
const AddressSchema = createSchema({city: Type.string()}, { _id: false, timestamps: true }); | ||
const AddressSchema = createSchema( | ||
{ city: Type.string() }, | ||
{ _id: false, timestamps: true } | ||
); | ||
{ | ||
// same as {type: AddressSchema} | ||
address: Type.schema().of(AddressSchema) | ||
address: Type.schema().of(AddressSchema); | ||
} | ||
// address property has city property, other Subdocument methods and properties except '_id' | ||
``` | ||
- `array.of(ExampleSchema)` will return DocumentArray instead of standard array | ||
```ts | ||
const PhoneSchema = createSchema({phoneNumber: Type.number()}, { _id: false }); | ||
const PhoneSchema = createSchema( | ||
{ phoneNumber: Type.number() }, | ||
{ _id: false } | ||
); | ||
{ | ||
// same as {type: [PhoneSchema]} | ||
phones: Type.schema().of(PhoneSchema) | ||
phones: Type.schema().of(PhoneSchema); | ||
} | ||
// phones property has such methods as create(), id(), but also those typical for arrays like map(), filter() etc | ||
``` | ||
- `ref` is a special type for creating references | ||
```ts | ||
@@ -204,11 +229,12 @@ { | ||
``` | ||
- `populateTs(property: string)` use this function to populate a property and adjust the returned type automatically. Under the hood it calls only the native `populate` method. | ||
Method will be available if you import a special plugin. | ||
Method will be available if you import a special plugin. | ||
```ts | ||
// models.ts | ||
import 'ts-mongoose/plugin' | ||
import 'ts-mongoose/plugin'; | ||
User.find().populateTs('comments'); | ||
``` | ||
@@ -223,3 +249,9 @@ | ||
```ts | ||
import { createSchema, Type, typedModel, ExtractDoc, ExtractProps } from 'ts-mongoose'; | ||
import { | ||
createSchema, | ||
Type, | ||
typedModel, | ||
ExtractDoc, | ||
ExtractProps, | ||
} from 'ts-mongoose'; | ||
@@ -236,5 +268,4 @@ export const UserSchema = createSchema({ | ||
// example function | ||
// example function | ||
async function blockUser(user: UserDoc) { | ||
@@ -252,16 +283,15 @@ user.isBlocked = true; | ||
username: 'user1', | ||
} | ||
}; | ||
} | ||
``` | ||
## Refs | ||
Refs and populations are supported. | ||
Check code under `example/example4.ts`. | ||
Check code under `example/example4.ts`. | ||
![alt autocomplete](.github/refs.gif) | ||
### Custom Field | ||
### Custom Field | ||
If you need to specify custom fields in the model, you can add a fake annotation. | ||
@@ -281,11 +311,39 @@ It's only required if you add virtual fields or custom methods to the model. | ||
``` | ||
Autocomplete popup: | ||
![alt autocomplete](.github/custom.png) | ||
### Static methods | ||
If you need to have static custom methods on Model you can pass them in `statics` property, which is part of schema options. For functions returning instance/s of Model, use `ModelInstanceType` / `ModelInstancesType` interfaces as returning value. | ||
```ts | ||
const UserSchema = createSchema( | ||
{ | ||
name: Type.string(), | ||
age: Type.number(), | ||
}, | ||
{ | ||
statics: { | ||
findByName: function(name: string): ModelInstancesType { | ||
return this.find({ name: name }); | ||
}, | ||
findOneByName: function(name: string): ModelInstanceType { | ||
return this.findOne({ name: name }); | ||
}, | ||
countLetters: function(name: string, bonus?: number): number { | ||
return name.length + (bonus ? bonus : 0); | ||
}, | ||
}, | ||
} | ||
); | ||
const User = typedModel('User', UserSchema); | ||
User.countLetters('a'); | ||
``` | ||
### TODO | ||
- support types: Decimal128, Map | ||
MIT | ||
MIT |
import { Schema, Document, Model } from 'mongoose'; | ||
import { Extract } from './types'; | ||
export declare function typedModel<T extends Schema>(name: string, schema?: T, collection?: string, skipInit?: boolean): Model<Document & Extract<T>>; | ||
import { Extract, ExtractStatics } from './types'; | ||
export declare function typedModel<T extends Schema>(name: string, schema?: T, collection?: string, skipInit?: boolean): ExtractStatics<T, Model<Document & Extract<T>>, Document & Extract<T>>; |
@@ -82,2 +82,13 @@ import { Types, Document } from 'mongoose'; | ||
export declare type TypeWithTimestamps<Opts, T> = Opts extends (TimestampsPresent | TimestampsEachPresent) ? T & CreatedAtType & UpdatedAtType : Opts extends TimestampCreatedByPresent ? T & CreatedAtType : Opts extends TimestampUpdatedByPresent ? T & UpdatedAtType : T; | ||
export declare type ModelInstanceType = 'ModelInstanceType'; | ||
export declare type ModelInstancesType = 'ModelInstancesType'; | ||
declare type ArgumentTypes<T> = T extends (...args: infer U) => infer R ? U : never; | ||
declare type ReplaceReturnType<T, TNewReturn> = (...a: ArgumentTypes<T>) => TNewReturn; | ||
export declare type ExtractStatics<T, Rest, I> = T extends { | ||
options: infer O; | ||
} ? O extends { | ||
statics: infer S; | ||
} ? { | ||
[P in keyof S]: S[P] extends (...args: any) => any ? ReturnType<S[P]> extends ModelInstanceType ? ReplaceReturnType<S[P], Promise<null | I>> : ReturnType<S[P]> extends ModelInstancesType ? ReplaceReturnType<S[P], Promise<I[]>> : S[P] : S[P]; | ||
} & Rest : Rest : Rest; | ||
export {}; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
127515
956
339
0