From 2a3f96549d3c09e3c4aeb38cadd5bbd557fe16cc Mon Sep 17 00:00:00 2001 From: D-Sketon <2055272094@qq.com> Date: Sun, 30 Jun 2024 21:37:57 +0800 Subject: [PATCH 1/4] refactor: refactor types --- src/database.ts | 4 +- src/document.ts | 4 +- src/model.ts | 112 ++++++++++++++++++++++++---------- src/query.ts | 36 ++++++----- src/schema.ts | 20 +++--- src/schematype.ts | 2 +- src/types.ts | 21 +++---- src/util.ts | 18 +++--- test/scripts/database.ts | 4 +- test/scripts/document.ts | 8 +-- test/scripts/model.ts | 14 ++--- test/scripts/mutex.ts | 2 +- test/scripts/query.ts | 20 +++--- test/scripts/schema.ts | 2 +- test/scripts/schematype.ts | 6 +- test/scripts/types/array.ts | 12 ++-- test/scripts/types/boolean.ts | 4 +- test/scripts/types/buffer.ts | 6 +- test/scripts/types/cuid.ts | 6 +- test/scripts/types/date.ts | 6 +- test/scripts/types/enum.ts | 4 +- test/scripts/types/integer.ts | 4 +- test/scripts/types/number.ts | 4 +- test/scripts/types/object.ts | 2 +- test/scripts/types/string.ts | 4 +- test/scripts/types/virtual.ts | 2 +- test/scripts/util.ts | 84 ++++++++++++++----------- 27 files changed, 233 insertions(+), 178 deletions(-) diff --git a/src/database.ts b/src/database.ts index 8d8d031b..497769d8 100644 --- a/src/database.ts +++ b/src/database.ts @@ -74,8 +74,8 @@ async function exportAsync(database: Database, path: string): Promise { interface DatabaseOptions { version: number, path: string, - onUpgrade: (...args: any[]) => any, - onDowngrade: (...args: any[]) => any + onUpgrade: (oldVersion: number, newVersion: number) => any, + onDowngrade: (oldVersion: number, newVersion: number) => any } class Database { diff --git a/src/document.ts b/src/document.ts index e6d12e74..dfe5479f 100644 --- a/src/document.ts +++ b/src/document.ts @@ -2,7 +2,7 @@ import rfdc from 'rfdc'; import type Model from './model'; import type Schema from './schema'; import type BluebirdPromise from 'bluebird'; -import type { NodeJSLikeCallback } from './types'; +import type { NodeJSLikeCallback, Options } from './types'; const cloneDeep = rfdc(); abstract class Document { @@ -98,7 +98,7 @@ abstract class Document { * @param {String|Object} expr * @return {Document} */ - populate(expr: string | any[] | { path?: string; model?: any; [key: PropertyKey]: any }): Document { + populate(expr: string | string[] | Partial[] | Partial): Document { const stack = this._schema._parsePopulate(expr); return this._model._populate(this, stack); } diff --git a/src/model.ts b/src/model.ts index a61fef58..adbd5999 100644 --- a/src/model.ts +++ b/src/model.ts @@ -11,7 +11,7 @@ import WarehouseError from './error'; import PopulationError from './error/population'; import Mutex from './mutex'; import type Database from './database'; -import type { AddSchemaTypeOptions, NodeJSLikeCallback, Options, PopulateResult } from './types'; +import type { AddSchemaTypeOptions, NodeJSLikeCallback, Options } from './types'; class Model extends EventEmitter { _mutex = new Mutex(); @@ -100,7 +100,12 @@ class Model extends EventEmitter { * @param {boolean} [options.lean=false] Returns a plain JavaScript object * @return {Document|object} */ - findById(id: PropertyKey, options_?: Options): Document | T { + findById(id: PropertyKey): Document; + findById(id: PropertyKey, options_: Partial> & { lean: true }): T; + findById(id: PropertyKey, options_: Partial> & { lean: false }): Document; + findById(id: PropertyKey, options_: Partial> & { lean?: undefined }): Document; + findById(id: PropertyKey, options_: Partial): Document | T; + findById(id: PropertyKey, options_?: Partial): Document | T { const raw = this.data[id]; if (!raw) return; @@ -421,13 +426,18 @@ class Model extends EventEmitter { * @param {function} iterator * @param {object} [options] See {@link Model#findById}. */ - forEach(iterator: (value: any, index: number) => void, options?: Options): void { + forEach(iterator: (value: Document, index: number) => void): void; + forEach(iterator: (value: T, index: number) => void, options: Partial> & { lean: true }): void; + forEach(iterator: (value: Document, index: number) => void, options: Partial> & { lean: false }): void; + forEach(iterator: (value: Document, index: number) => void, options: Partial> & { lean?: undefined }): void; + forEach(iterator: ((value: T, index: number) => void) | ((value: Document, index: number) => void), options: Partial): void; + forEach(iterator: ((value: T, index: number) => void) | ((value: Document, index: number) => void), options?: Partial): void { const keys = Object.keys(this.data); let num = 0; for (let i = 0, len = keys.length; i < len; i++) { const data = this.findById(keys[i], options); - if (data) iterator(data, num++); + if (data) iterator(data as any, num++); } } @@ -437,10 +447,15 @@ class Model extends EventEmitter { * @param {Object} [options] See {@link Model#findById}. * @return {Array} */ - toArray(options?: Options): any[] { + toArray(): Document[]; + toArray(options: Partial> & { lean: true }): T[]; + toArray(options: Partial> & { lean: false }): Document[]; + toArray(options: Partial> & { lean?: undefined }): Document[]; + toArray(options: Partial): Document[] | T[]; + toArray(options?: Partial): Document[] | T[] { const result = new Array(this.length); - this.forEach((item, i) => { + this.forEach((item, i: number) => { result[i] = item; }, options); @@ -458,15 +473,18 @@ class Model extends EventEmitter { * @return {Query|Array} */ find(query: object): Query; - find(query: object, options: Options): Query | T[]; - find(query: object, options: Options = {}): Query | T[] { + find(query: object, options: Partial> & { lean: true }): T[]; + find(query: object, options: Partial> & { lean: false }): Query; + find(query: object, options: Partial> & { lean?: undefined }): Query; + find(query: object, options: Partial): Query | T[]; + find(query: object, options: Partial = {}): Query | T[] { const filter = this.schema._execQuery(query); const keys = Object.keys(this.data); const len = keys.length; let limit = options.limit || this.length; let skip = options.skip; const data = this.data; - const arr: (T | Document)[] = []; + const arr: T[] | Document[] = []; for (let i = 0; limit && i < len; i++) { const key = keys[i]; @@ -476,7 +494,7 @@ class Model extends EventEmitter { if (skip) { skip--; } else { - arr.push(this.findById(key, options)); + arr.push(this.findById(key, options) as any); limit--; } } @@ -495,12 +513,15 @@ class Model extends EventEmitter { * @return {Document|Object} */ findOne(query: object): Document; - findOne(query: object, options_ : Options): Document | T; - findOne(query: object, options_ : Options = {}): Document | T { + findOne(query: object, options_: Partial> & { lean: true }): T; + findOne(query: object, options_: Partial> & { lean: false }): Document; + findOne(query: object, options_: Partial> & { lean?: undefined }): Document; + findOne(query: object, options_ : Partial): Document | T; + findOne(query: object, options_ : Partial = {}): Document | T { const options = Object.assign(options_, { limit: 1 }); const result = this.find(query, options); - return options.lean ? (result as any[])[0] : (result as Query).toArray()[0]; + return options.lean ? (result as T[])[0] : (result as Query).toArray()[0]; } /** @@ -510,7 +531,7 @@ class Model extends EventEmitter { * @param {String|Number} [order] * @return {Query} */ - sort(orderby: string | object, order?: string | number): Query { + sort(orderby: string | object, order?: string | number | object): Query { const sort = parseArgs(orderby, order); const fn = this.schema._execSort(sort); @@ -525,7 +546,12 @@ class Model extends EventEmitter { * @param {Object} [options] See {@link Model#findById}. * @return {Document|Object} */ - eq(i_: number, options?: Options): Document | Record { + eq(i_: number): Document; + eq(i_: number, options: Partial> & { lean: true }): T; + eq(i_: number, options: Partial> & { lean: false }): Document; + eq(i_: number, options: Partial> & { lean?: undefined }): Document; + eq(i_: number, options: Partial): Document | T; + eq(i_: number, options?: Partial): Document | T { let index = i_ < 0 ? this.length + i_ : i_; const data = this.data; const keys = Object.keys(data); @@ -550,7 +576,12 @@ class Model extends EventEmitter { * @param {Object} [options] See {@link Model#findById}. * @return {Document|Object} */ - first(options?: Options): Document | Record { + first(): Document; + first(options: Partial> & { lean: true }): T; + first(options: Partial> & { lean: false }): Document; + first(options: Partial> & { lean?: undefined }): Document; + first(options: Partial): Document | T; + first(options?: Partial): Document | T { return this.eq(0, options); } @@ -560,7 +591,12 @@ class Model extends EventEmitter { * @param {Object} [options] See {@link Model#findById}. * @return {Document|Object} */ - last(options?: Options): Document | Record { + last(): Document; + last(options: Partial> & { lean: true }): T; + last(options: Partial> & { lean: false }): Document; + last(options: Partial> & { lean?: undefined }): Document; + last(options: Partial): Document | T; + last(options?: Partial): Document | T { return this.eq(-1, options); } @@ -649,7 +685,12 @@ class Model extends EventEmitter { * @param {Object} [options] * @return {Array} */ - map(iterator: (value: any, index: number) => T, options?: Options): T[] { + map(iterator: (value: Document, index: number) => R): R[]; + map(iterator: (value: T, index: number) => R, options: Partial> & { lean: true }): R[]; + map(iterator: (value: Document, index: number) => R, options: Partial> & { lean: false }): R[]; + map(iterator: (value: Document, index: number) => R, options: Partial> & { lean?: undefined }): R[]; + map(iterator: ((value: T, index: number) => R) | ((value: Document, index: number) => R), options: Partial): R[]; + map(iterator: ((value: T, index: number) => R) | ((value: Document, index: number) => R), options?: Partial): R[] { const result = new Array(this.length); const keys = Object.keys(this.data); const len = keys.length; @@ -657,7 +698,7 @@ class Model extends EventEmitter { for (let i = 0, num = 0; i < len; i++) { const data = this.findById(keys[i], options); if (data) { - result[num] = iterator(data, num); + result[num] = iterator(data as any, num); num++; } } @@ -673,7 +714,7 @@ class Model extends EventEmitter { * @param {*} [initial] By default, the initial value is the first document. * @return {*} */ - reduce(iterator: (pre: any, cur: any, index: number) => T, initial?: T): T { + reduce(iterator: (pre: any, cur: Document, index: number) => R, initial?: R): R { const arr = this.toArray(); const len = this.length; let i: number, result: any; @@ -701,10 +742,10 @@ class Model extends EventEmitter { * @param {*} [initial] By default, the initial value is the last document. * @return {*} */ - reduceRight(iterator: (pre: any, cur: any, index: number) => T, initial?: T): T { + reduceRight(iterator: (pre: any, cur: Document, index: number) => R, initial?: R): R { const arr = this.toArray(); const len = this.length; - let i, result; + let i: number, result: any; if (initial === undefined) { i = len - 2; @@ -729,7 +770,12 @@ class Model extends EventEmitter { * @param {Object} [options] * @return {Query} */ - filter(iterator: (value: any, index: number) => any, options?: Options): Query { + filter(iterator: (value: Document, index: number) => boolean): Query; + filter(iterator: (value: T, index: number) => boolean, options: Partial> & { lean: true }): Query; + filter(iterator: (value: Document, index: number) => boolean, options: Partial> & { lean: false }): Query; + filter(iterator: (value: Document, index: number) => boolean, options: Partial> & { lean?: undefined }): Query; + filter(iterator: ((value: T, index: number) => boolean) | ((value: Document, index: number) => boolean), options: Partial): Query; + filter(iterator: ((value: T, index: number) => boolean) | ((value: Document, index: number) => boolean), options?: Partial): Query { const arr = []; this.forEach((item: any, i: number) => { @@ -746,7 +792,7 @@ class Model extends EventEmitter { * @param {Function} iterator * @return {Boolean} */ - every(iterator: (value: any, index: number) => any): boolean { + every(iterator: (value: Document, index: number) => boolean): boolean { const keys = Object.keys(this.data); const len = keys.length; let num = 0; @@ -771,7 +817,7 @@ class Model extends EventEmitter { * @param {Function} iterator * @return {Boolean} */ - some(iterator: (value: any, index: number) => any): boolean { + some(iterator: (value: Document, index: number) => boolean): boolean { const keys = Object.keys(this.data); const len = keys.length; let num = 0; @@ -798,9 +844,9 @@ class Model extends EventEmitter { * @return {Function} * @private */ - _populateGetter(data: string | number, model: Model, options: unknown) { + _populateGetter(data: PropertyKey, model: Model, options: unknown): () => Document { let hasCache = false; - let cache: Record | Document; + let cache: Document; return () => { if (!hasCache) { @@ -821,14 +867,14 @@ class Model extends EventEmitter { * @return {Function} * @private */ - _populateGetterArray(data: any[], model: Model, options: Options): () => any[] | Query { + _populateGetterArray(data: PropertyKey[], model: Model, options: Partial): () => T[] | Query { const Query = model.Query; let hasCache = false; - let cache: any[] | Query; + let cache: T[] | Query; return () => { if (!hasCache) { - let arr = []; + let arr: Document[] = []; for (let i = 0, len = data.length; i < len; i++) { arr.push(model.findById(data[i])); @@ -869,7 +915,7 @@ class Model extends EventEmitter { * @return {Object} * @private */ - _populate(data: Document, stack: PopulateResult[]): Document { + _populate(data: Document, stack: Partial[]): Document { const models = this._database._models; for (let i = 0, len = stack.length; i < len; i++) { @@ -899,7 +945,7 @@ class Model extends EventEmitter { * @param {String|Object} path * @return {Query} */ - populate(path: string | any[] | { path?: string; model?: any; [key: PropertyKey]: any }): Query { + populate(path: string | string[] | Partial[] | Partial): Query { if (!path) throw new TypeError('path is required'); const stack = this.schema._parsePopulate(path); @@ -963,7 +1009,7 @@ class Model extends EventEmitter { Model.prototype.get = Model.prototype.findById; -function execHooks(schema: Schema, type: string, event: string, data: any): BluebirdPromise { +function execHooks(schema: Schema, type: keyof Schema['hooks'], event: keyof Schema['hooks'][keyof Schema['hooks']], data: any): BluebirdPromise { const hooks = schema.hooks[type][event] as ((data: any) => BluebirdPromise | void)[]; if (!hooks.length) return BluebirdPromise.resolve(data); diff --git a/src/query.ts b/src/query.ts index 93ba80c7..dae0f58b 100644 --- a/src/query.ts +++ b/src/query.ts @@ -35,7 +35,7 @@ abstract class Query { * * @param {Function} iterator */ - forEach(iterator: (item: any, index: number) => void): void { + forEach(iterator: (item: Document, index: number) => void): void { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -142,8 +142,11 @@ abstract class Query { * @return {Query|Array} */ find(query: object): Query; - find(query: object, options: Options): T[] | Query; - find(query: object, options: Options = {}): T[] | Query { + find(query: object, options: Partial> & { lean: true }): T[]; + find(query: object, options: Partial> & { lean: false }): Query; + find(query: object, options: Partial> & { lean?: undefined }): Query; + find(query: object, options: Partial): T[] | Query; + find(query: object, options: Partial = {}): T[] | Query { const filter = this._schema._execQuery(query); const { data, length } = this; const { lean = false } = options; @@ -176,8 +179,11 @@ abstract class Query { * @return {Document|Object} */ findOne(query: object): Document; - findOne(query: object, options): Document | T; - findOne(query: object, options: Options = {}): Document | T { + findOne(query: object, options: Partial> & { lean: true }): T; + findOne(query: object, options: Partial> & { lean: false }): Document; + findOne(query: object, options: Partial> & { lean?: undefined }): Document; + findOne(query: object, options: Partial): Document | T; + findOne(query: object, options: Partial = {}): Document | T { const _options = Object.assign(options, { limit: 1 }); const result = this.find(query, _options); @@ -215,9 +221,9 @@ abstract class Query { * @param {Function} iterator * @return {Array} */ - map(iterator: (item: any, index: number) => T): T[] { + map(iterator: (item: Document, index: number) => R): R[] { const { data, length } = this; - const result: T[] = new Array(length); + const result: R[] = new Array(length); for (let i = 0; i < length; i++) { result[i] = iterator(data[i], i); @@ -234,7 +240,7 @@ abstract class Query { * @param {*} [initial] By default, the initial value is the first document. * @return {*} */ - reduce(iterator: (pre: any, cur: any, index: number) => R, initial?: R): R { + reduce(iterator: (pre: any, cur: Document, index: number) => R, initial?: R): R { const { data, length } = this; let result, i: number; @@ -261,7 +267,7 @@ abstract class Query { * @param {*} [initial] By default, the initial value is the last document. * @return {*} */ - reduceRight(iterator: (pre: any, cur: any, index: number) => R, initial?: R): R { + reduceRight(iterator: (pre: any, cur: Document, index: number) => R, initial?: R): R { const { data, length } = this; let result, i; @@ -287,7 +293,7 @@ abstract class Query { * @param {Function} iterator * @return {Query} */ - filter(iterator: (item: any, index: number) => boolean): Query { + filter(iterator: (item: Document, index: number) => boolean): Query { const { data, length } = this; const arr = []; @@ -306,7 +312,7 @@ abstract class Query { * @param {Function} iterator * @return {Boolean} */ - every(iterator: (item: any, index: number) => boolean): boolean { + every(iterator: (item: Document, index: number) => boolean): boolean { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -323,7 +329,7 @@ abstract class Query { * @param {Function} iterator * @return {Boolean} */ - some(iterator: (item: any, index: number) => boolean): boolean { + some(iterator: (item: Document, index: number) => boolean): boolean { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -340,7 +346,7 @@ abstract class Query { * @param {Function} [callback] * @return {BluebirdPromise} */ - update(data: any, callback?: NodeJSLikeCallback): BluebirdPromise { + update(data: object, callback?: NodeJSLikeCallback): BluebirdPromise { const model = this._model; const stack = this._schema._parseUpdate(data); @@ -354,7 +360,7 @@ abstract class Query { * @param {Function} [callback] * @return {BluebirdPromise} */ - replace(data: any, callback?: NodeJSLikeCallback): BluebirdPromise { + replace(data: Document | T, callback?: NodeJSLikeCallback): BluebirdPromise { const model = this._model; return BluebirdPromise.map(this.data, item => model.replaceById(item._id, data)).asCallback(callback); @@ -378,7 +384,7 @@ abstract class Query { * @param {String|Object} expr * @return {Query} */ - populate(expr: string | any[] | { path?: string; model?: any; [key: PropertyKey]: any }): Query { + populate(expr: string | string[] | Partial[] | Partial): Query { const stack = this._schema._parsePopulate(expr); const { data, length } = this; const model = this._model; diff --git a/src/schema.ts b/src/schema.ts index f6814a59..d6229d6e 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -5,9 +5,8 @@ import { getProp, setProp, delProp } from './util'; import PopulationError from './error/population'; import SchemaTypeVirtual from './types/virtual'; import { isPlainObject } from 'is-plain-object'; -import type { AddSchemaTypeLoopOptions, AddSchemaTypeOptions, PopulateResult, SchemaTypeOptions } from './types'; +import type { AddSchemaTypeLoopOptions, AddSchemaTypeOptions, Options, SchemaTypeOptions } from './types'; import type Model from './model'; -import type Document from './document'; /** * @callback queryFilterCallback @@ -387,12 +386,12 @@ class Schema { methods: Record any> = {}; hooks: { pre: { - save: ((...args: any[]) => BluebirdPromise)[] - remove: ((...args: any[]) => BluebirdPromise)[] + save: ((data: any) => BluebirdPromise)[] + remove: ((data: any) => BluebirdPromise)[] }; post: { - save: ((...args: any[]) => BluebirdPromise)[] - remove: ((...args: any[]) => BluebirdPromise)[] + save: ((data: any) => BluebirdPromise)[] + remove: ((data: any) => BluebirdPromise)[] }; }; stacks: { @@ -663,7 +662,7 @@ class Schema { * @return {Object} * @private */ - _parseDatabase(data: object): object { + _parseDatabase(data: object): any { const stack = this.stacks.import; for (let i = 0, len = stack.length; i < len; i++) { @@ -680,7 +679,7 @@ class Schema { * @return {Object} * @private */ - _exportDatabase(data: object): object { + _exportDatabase(data: object): any { const stack = this.stacks.export; for (let i = 0, len = stack.length; i < len; i++) { @@ -760,10 +759,9 @@ class Schema { * @return {PopulateResult[]} * @private */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - _parsePopulate(expr: string | any[] | { path?: string; model?: any; [key: PropertyKey]: any }): PopulateResult[] { + _parsePopulate(expr: string | string[] | Partial[] | Partial): Partial[] { const { paths } = this; - const arr = []; + const arr: Partial[] = []; if (typeof expr === 'string') { const split = expr.split(' '); diff --git a/src/schematype.ts b/src/schematype.ts index 0ba0b6d2..a88e3a4f 100644 --- a/src/schematype.ts +++ b/src/schematype.ts @@ -278,7 +278,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - u$rename(value: unknown, update: unknown, data: unknown): void { + u$rename(value: unknown, update: string, data: unknown): void { if (value !== undefined) setProp(data, update, value); return undefined; } diff --git a/src/types.ts b/src/types.ts index 21f6107f..16d85d98 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,10 +3,13 @@ import type SchemaType from './schematype'; export type NodeJSLikeCallback = (err: E, result?: R) => void export interface Options { - lean?: boolean; - skip?: number; - limit?: number; - [key: PropertyKey]: any; + lean: boolean; + skip: number; + limit: number; + match: object; + sort: any; + path: string; + model: string; } export type SchemaTypeOptions = typeof SchemaType | SchemaType | ((...args: any[]) => any) @@ -20,13 +23,3 @@ export interface AddSchemaTypeLoopOptions { } export type AddSchemaTypeOptions = AddSchemaTypeMixedOptions | AddSchemaTypeLoopOptions; - -/** - * @typedef PopulateResult - * @property {string} path - * @property {*} model - */ -export type PopulateResult = { - path: string; - model: any; -}; diff --git a/src/util.ts b/src/util.ts index b7ab5d57..aabb5188 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,7 +2,7 @@ function extractPropKey(key: string): string[] { return key.split('.'); } -function _parseArgs(args: string) { +function _parseArgs(args: string): Record { if (typeof args !== 'string') return args; const arr = args.split(' '); @@ -29,7 +29,7 @@ function _parseArgs(args: string) { } -export function shuffle(array) { +export function shuffle(array: T[]): T[] { if (!Array.isArray(array)) throw new TypeError('array must be an Array!'); const $array = array.slice(); const { length } = $array; @@ -48,7 +48,7 @@ export function shuffle(array) { return $array; } -export function getProp(obj, key) { +export function getProp(obj: Record, key: string): any { if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); if (!key) throw new TypeError('key is required!'); @@ -67,7 +67,7 @@ export function getProp(obj, key) { return result; } -export function setProp(obj, key, value) { +export function setProp(obj: Record, key: string, value: any): void { if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); if (!key) throw new TypeError('key is required!'); @@ -91,7 +91,7 @@ export function setProp(obj, key, value) { cursor[lastKey] = value; } -export function delProp(obj, key) { +export function delProp(obj: Record, key: string): void { if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); if (!key) throw new TypeError('key is required!'); @@ -119,7 +119,7 @@ export function delProp(obj, key) { delete cursor[lastKey]; } -export function setGetter(obj, key, fn) { +export function setGetter(obj: Record, key: string, fn: () => any): void { if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); if (!key) throw new TypeError('key is required!'); if (typeof fn !== 'function') throw new TypeError('fn must be a function!'); @@ -144,7 +144,7 @@ export function setGetter(obj, key, fn) { cursor.__defineGetter__(lastKey, fn); } -export function arr2obj(arr, value) { +export function arr2obj(arr: string[], value: T): Record { if (!Array.isArray(arr)) throw new TypeError('arr must be an array!'); const obj = {}; @@ -157,7 +157,7 @@ export function arr2obj(arr, value) { return obj; } -export function reverse(arr) { +export function reverse(arr: T[]): T[] { if (!Array.isArray(arr)) throw new TypeError('arr must be an array!'); const len = arr.length; @@ -173,7 +173,7 @@ export function reverse(arr) { return arr; } -export function parseArgs(orderby: string | object, order?: string | number | object) { +export function parseArgs(orderby: string | object, order?: string | number | object): Record { let result; if (order) { diff --git a/test/scripts/database.ts b/test/scripts/database.ts index a062ea5e..7edafe93 100644 --- a/test/scripts/database.ts +++ b/test/scripts/database.ts @@ -3,8 +3,8 @@ const should = chai.should(); // eslint-disable-line import path from 'path'; import Promise from 'bluebird'; import sinon from 'sinon'; -import Database from '../../dist/database'; -import Model from '../../dist/model'; +import Database from '../../src/database'; +import Model from '../../src/model'; import fs from 'fs'; const promisifyFs = Promise.promisifyAll(fs); diff --git a/test/scripts/document.ts b/test/scripts/document.ts index 565215ff..ac9fd00a 100644 --- a/test/scripts/document.ts +++ b/test/scripts/document.ts @@ -1,8 +1,8 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line -import Database from '../../dist/database'; -import Document from '../../dist/document'; -import type Model from '../../dist/model'; +const should = chai.should(); +import Database from '../../src/database'; +import Document from '../../src/document'; +import type Model from '../../src/model'; interface UserType { name?: string; diff --git a/test/scripts/model.ts b/test/scripts/model.ts index 3d9b59d7..abf0a70c 100644 --- a/test/scripts/model.ts +++ b/test/scripts/model.ts @@ -1,17 +1,17 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line +const should = chai.should(); import chaiAsPromised from 'chai-as-promised'; -chai.use(chaiAsPromised ); // eslint-disable-line +chai.use(chaiAsPromised); import lodash from 'lodash'; const { sortBy } = lodash; import Promise from 'bluebird'; import sinon from 'sinon'; import { nanoid } from 'nanoid'; -import Database from '../../dist/database'; -import type Query from '../../dist/query'; -import type Document from '../../dist/document'; -import type Model from '../../dist/model'; +import Database from '../../src/database'; +import type Query from '../../src/query'; +import type Document from '../../src/document'; +import type Model from '../../src/model'; interface UserType { name?: { @@ -99,7 +99,7 @@ describe('Model', () => { email: 'abc@example.com', age: 20 }).then(data => { - User.findById(data._id, {lean: true}).name.should.not.ownProperty('full'); + User.findById(data._id, {lean: true}).name!.should.not.ownProperty('full'); return data; }).then(data => User.removeById(data._id))); diff --git a/test/scripts/mutex.ts b/test/scripts/mutex.ts index 7157285a..3d698b62 100644 --- a/test/scripts/mutex.ts +++ b/test/scripts/mutex.ts @@ -1,6 +1,6 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import Mutex from '../../dist/mutex'; +import Mutex from '../../src/mutex'; import sinon from 'sinon'; describe('Mutex', () => { diff --git a/test/scripts/query.ts b/test/scripts/query.ts index af5adaa1..74f3f55b 100644 --- a/test/scripts/query.ts +++ b/test/scripts/query.ts @@ -1,17 +1,17 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line +const should = chai.should(); import lodash from 'lodash'; const { sortBy } = lodash; import Promise from 'bluebird'; -import Document from '../../dist/document'; -import Database from '../../dist/database'; -import type Query from '../../dist/query'; -import type Model from '../../dist/model'; +import Document from '../../src/document'; +import Database from '../../src/database'; +import type Model from '../../src/model'; interface UserType { name?: string; age?: number; comments?: string; + _id?: string; } interface LoopType { @@ -165,7 +165,7 @@ describe('Query', () => { {age: 30}, {age: 40} ]).then(data => { - const query = User.find({}).find({age: {$gte: 20}}, {limit: 2}) as Query; + const query = User.find({}).find({age: {$gte: 20}}, {limit: 2}); query.data.should.eql(data.slice(1, 3)); return data; }).map(item => User.removeById(item._id))); @@ -176,11 +176,11 @@ describe('Query', () => { {age: 30}, {age: 40} ]).then(data => { - let query = User.find({}).find({age: {$gte: 20}}, {skip: 1}) as Query; + let query = User.find({}).find({age: {$gte: 20}}, {skip: 1}); query.data.should.eql(data.slice(2)); // with limit - query = User.find({}).find({age: {$gte: 20}}, {limit: 1, skip: 1}) as Query; + query = User.find({}).find({age: {$gte: 20}}, {limit: 1, skip: 1}); query.data.should.eql(data.slice(2, 3)); return data; @@ -192,7 +192,7 @@ describe('Query', () => { {age: 30}, {age: 40} ]).then(data => { - const query = User.find({}).find({age: {$gt: 20}}, {lean: true}) as Query; + const query = User.find({}).find({age: {$gt: 20}}, {lean: true}); query.should.be.a('array'); const { length } = query; for (let i = 0; i < length; i++) { @@ -332,7 +332,7 @@ describe('Query', () => { {age: 30}, {age: 40} ]).then(data => { - const result = User.find({}).findOne({age: {$gt: 20}}, {lean: true}) as Document; + const result = User.find({}).findOne({age: {$gt: 20}}, {lean: true}); result._id!.should.eql(data[2]._id); result.should.to.not.be.an.instanceof(Document); return data; diff --git a/test/scripts/schema.ts b/test/scripts/schema.ts index 5e90e17c..abf9e5bb 100644 --- a/test/scripts/schema.ts +++ b/test/scripts/schema.ts @@ -1,6 +1,6 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import Database from '../../dist/database'; +import Database from '../../src/database'; describe('Schema', () => { const Schema = Database.Schema; diff --git a/test/scripts/schematype.ts b/test/scripts/schematype.ts index 39abb55f..7f0f3e7c 100644 --- a/test/scripts/schematype.ts +++ b/test/scripts/schematype.ts @@ -1,7 +1,7 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line -import ValidationError from '../../dist/error/validation'; -import SchemaType from '../../dist/schematype'; +const should = chai.should(); +import ValidationError from '../../src/error/validation'; +import SchemaType from '../../src/schematype'; describe('SchemaType', () => { const type = new SchemaType('test'); diff --git a/test/scripts/types/array.ts b/test/scripts/types/array.ts index 61cd52ba..1ad7263a 100644 --- a/test/scripts/types/array.ts +++ b/test/scripts/types/array.ts @@ -1,10 +1,10 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeArray from '../../../dist/types/array'; -import SchemaTypeString from '../../../dist/types/string'; -import SchemaTypeDate from '../../../dist/types/date'; -import SchemaTypeBoolean from '../../../dist/types/boolean'; +const should = chai.should(); +import ValidationError from '../../../src/error/validation'; +import SchemaTypeArray from '../../../src/types/array'; +import SchemaTypeString from '../../../src/types/string'; +import SchemaTypeDate from '../../../src/types/date'; +import SchemaTypeBoolean from '../../../src/types/boolean'; describe('SchemaTypeArray', () => { const type = new SchemaTypeArray('test'); diff --git a/test/scripts/types/boolean.ts b/test/scripts/types/boolean.ts index bf84920c..21f4922f 100644 --- a/test/scripts/types/boolean.ts +++ b/test/scripts/types/boolean.ts @@ -1,7 +1,7 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeBoolean from '../../../dist/types/boolean'; +import ValidationError from '../../../src/error/validation'; +import SchemaTypeBoolean from '../../../src/types/boolean'; describe('SchemaTypeBoolean', () => { const type = new SchemaTypeBoolean('test'); diff --git a/test/scripts/types/buffer.ts b/test/scripts/types/buffer.ts index 90ee44c9..97cf37c8 100644 --- a/test/scripts/types/buffer.ts +++ b/test/scripts/types/buffer.ts @@ -1,7 +1,7 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeBuffer from '../../../dist/types/buffer'; +const should = chai.should(); +import ValidationError from '../../../src/error/validation'; +import SchemaTypeBuffer from '../../../src/types/buffer'; describe('SchemaTypeBuffer', () => { const type = new SchemaTypeBuffer('test'); diff --git a/test/scripts/types/cuid.ts b/test/scripts/types/cuid.ts index 86a4aa9e..a3448f31 100644 --- a/test/scripts/types/cuid.ts +++ b/test/scripts/types/cuid.ts @@ -1,8 +1,8 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line +const should = chai.should(); import { nanoid } from 'nanoid'; -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeCUID from '../../../dist/types/cuid'; +import ValidationError from '../../../src/error/validation'; +import SchemaTypeCUID from '../../../src/types/cuid'; describe('SchemaTypeCUID', () => { const type = new SchemaTypeCUID('test'); diff --git a/test/scripts/types/date.ts b/test/scripts/types/date.ts index 49a55ca3..9a1e26ed 100644 --- a/test/scripts/types/date.ts +++ b/test/scripts/types/date.ts @@ -1,7 +1,7 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeDate from '../../../dist/types/date'; +const should = chai.should(); +import ValidationError from '../../../src/error/validation'; +import SchemaTypeDate from '../../../src/types/date'; describe('SchemaTypeDate', () => { const type = new SchemaTypeDate('test'); diff --git a/test/scripts/types/enum.ts b/test/scripts/types/enum.ts index 7804322b..709f04a9 100644 --- a/test/scripts/types/enum.ts +++ b/test/scripts/types/enum.ts @@ -1,7 +1,7 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeEnum from '../../../dist/types/enum'; +import ValidationError from '../../../src/error/validation'; +import SchemaTypeEnum from '../../../src/types/enum'; describe('SchemaTypeEnum', () => { it('validate()', () => { diff --git a/test/scripts/types/integer.ts b/test/scripts/types/integer.ts index ffe341f2..98fb46fb 100644 --- a/test/scripts/types/integer.ts +++ b/test/scripts/types/integer.ts @@ -1,7 +1,7 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeInteger from '../../../dist/types/integer'; +import ValidationError from '../../../src/error/validation'; +import SchemaTypeInteger from '../../../src/types/integer'; describe('SchemaTypeInteger', () => { const type = new SchemaTypeInteger('test'); diff --git a/test/scripts/types/number.ts b/test/scripts/types/number.ts index 3dd6003b..0f191dac 100644 --- a/test/scripts/types/number.ts +++ b/test/scripts/types/number.ts @@ -1,7 +1,7 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeNumber from '../../../dist/types/number'; +import ValidationError from '../../../src/error/validation'; +import SchemaTypeNumber from '../../../src/types/number'; describe('SchemaTypeNumber', () => { const type = new SchemaTypeNumber('type'); diff --git a/test/scripts/types/object.ts b/test/scripts/types/object.ts index 960b651c..79b6b848 100644 --- a/test/scripts/types/object.ts +++ b/test/scripts/types/object.ts @@ -1,6 +1,6 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import SchemaTypeObject from '../../../dist/types/object'; +import SchemaTypeObject from '../../../src/types/object'; describe('SchemaTypeObject', () => { const type = new SchemaTypeObject('test'); diff --git a/test/scripts/types/string.ts b/test/scripts/types/string.ts index ebbda208..a2ebac08 100644 --- a/test/scripts/types/string.ts +++ b/test/scripts/types/string.ts @@ -1,7 +1,7 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import ValidationError from '../../../dist/error/validation'; -import SchemaTypeString from '../../../dist/types/string'; +import ValidationError from '../../../src/error/validation'; +import SchemaTypeString from '../../../src/types/string'; describe('SchemaTypeString', () => { const type = new SchemaTypeString('test'); diff --git a/test/scripts/types/virtual.ts b/test/scripts/types/virtual.ts index 117aad25..c95e5333 100644 --- a/test/scripts/types/virtual.ts +++ b/test/scripts/types/virtual.ts @@ -1,6 +1,6 @@ import chai from 'chai'; const should = chai.should(); // eslint-disable-line -import SchemaTypeVirtual from '../../../dist/types/virtual'; +import SchemaTypeVirtual from '../../../src/types/virtual'; describe('SchemaTypeVirtual', () => { const type = new SchemaTypeVirtual('test'); diff --git a/test/scripts/util.ts b/test/scripts/util.ts index b56f4b80..a5f7c5a8 100644 --- a/test/scripts/util.ts +++ b/test/scripts/util.ts @@ -1,11 +1,11 @@ import chai from 'chai'; -const should = chai.should(); // eslint-disable-line -import * as util from '../../dist/util'; +const should = chai.should(); +import { shuffle, getProp, setProp, delProp, setGetter, arr2obj, reverse, parseArgs } from '../../src/util'; describe('util', () => { it('shuffle()', () => { const src = Array(100).fill(0).map((_, i) => i); - const result = util.shuffle(src); + const result = shuffle(src); result.should.not.eql(src); result.should.to.have.members(src); @@ -15,7 +15,8 @@ describe('util', () => { }); it('shuffle() - array must be an array', () => { - (() => util.shuffle({})).should.to.throw('array must be an Array!'); + // @ts-expect-error + (() => shuffle({})).should.to.throw('array must be an Array!'); }); it('getProp()', () => { @@ -31,17 +32,19 @@ describe('util', () => { } }; - util.getProp(obj, 'a.b').should.eql(obj.a.b); - util.getProp(obj, 'c').should.eql(obj.c); - util.getProp(obj, 'd.e.f').should.eql(obj.d.e.f); + getProp(obj, 'a.b').should.eql(obj.a.b); + getProp(obj, 'c').should.eql(obj.c); + getProp(obj, 'd.e.f').should.eql(obj.d.e.f); }); it('getProp() - obj must be an object', () => { - (() => util.getProp('', null)).should.to.throw('obj must be an object!'); + // @ts-expect-error + (() => getProp('', null)).should.to.throw('obj must be an object!'); }); it('getProp() - key is required', () => { - (() => util.getProp({}, null)).should.to.throw('key is required!'); + // @ts-ignore + (() => getProp({}, null)).should.to.throw('key is required!'); }); it('setProp()', () => { @@ -57,22 +60,24 @@ describe('util', () => { } }; - util.setProp(obj, 'a.b', 10); + setProp(obj, 'a.b', 10); obj.a.b.should.eql(10); - util.setProp(obj, 'c', 20); + setProp(obj, 'c', 20); obj.c.should.eql(20); - util.setProp(obj, 'd.e.f', 'haha'); + setProp(obj, 'd.e.f', 'haha'); obj.d.e.f.should.eql('haha'); }); it('setProp() - obj must be an object', () => { - (() => util.setProp('', null, null)).should.to.throw('obj must be an object!'); + // @ts-expect-error + (() => setProp('', null, null)).should.to.throw('obj must be an object!'); }); it('setProp() - key is required', () => { - (() => util.setProp({}, null, null)).should.to.throw('key is required!'); + // @ts-ignore + (() => setProp({}, null, null)).should.to.throw('key is required!'); }); it('delProp()', () => { @@ -88,25 +93,27 @@ describe('util', () => { } }; - util.delProp(obj, 'a.b'); + delProp(obj, 'a.b'); should.not.exist(obj.a.b); - util.delProp(obj, 'c'); + delProp(obj, 'c'); should.not.exist(obj.c); - util.delProp(obj, 'd.e.f'); + delProp(obj, 'd.e.f'); should.not.exist(obj.d.e.f); - util.delProp(obj, 'd.f.g'); + delProp(obj, 'd.f.g'); should.exist(obj.d.e); }); it('delProp() - obj must be an object', () => { - (() => util.delProp('', null)).should.to.throw('obj must be an object!'); + // @ts-expect-error + (() => delProp('', null)).should.to.throw('obj must be an object!'); }); it('delProp() - key is required', () => { - (() => util.delProp({}, null)).should.to.throw('key is required!'); + // @ts-ignore + (() => delProp({}, null)).should.to.throw('key is required!'); }); it('setGetter()', () => { @@ -122,59 +129,64 @@ describe('util', () => { } }; - util.setGetter(obj, 'a.b', () => 100); + setGetter(obj, 'a.b', () => 100); obj.a.b.should.eql(100); - util.setGetter(obj, 'c', () => 200); + setGetter(obj, 'c', () => 200); obj.c.should.eql(200); - util.setGetter(obj, 'd.e.f', () => 'haha'); + setGetter(obj, 'd.e.f', () => 'haha'); obj.d.e.f.should.eql('haha'); - util.setGetter(obj, 'a.c.h', () => 'ach'); + setGetter(obj, 'a.c.h', () => 'ach'); // @ts-ignore obj.a.c.h.should.eql('ach'); }); it('setGetter() - obj must be an object', () => { - (() => util.setGetter('', null, null)).should.to.throw('obj must be an object!'); + // @ts-expect-error + (() => setGetter('', null, null)).should.to.throw('obj must be an object!'); }); it('setGetter() - key is required', () => { - (() => util.setGetter({}, null, null)).should.to.throw('key is required!'); + // @ts-ignore + (() => setGetter({}, null, null)).should.to.throw('key is required!'); }); it('setGetter() - fn must be a function', () => { - (() => util.setGetter({}, 'test', {})).should.to.throw('fn must be a function!'); + // @ts-expect-error + (() => setGetter({}, 'test', {})).should.to.throw('fn must be a function!'); }); it('arr2obj()', () => { - util.arr2obj(['a', 'b'], 1).should.eql({a: 1, b: 1}); + arr2obj(['a', 'b'], 1).should.eql({a: 1, b: 1}); }); it('arr2obj() - arr must be an array', () => { - (() => util.arr2obj({}, null)).should.to.throw('arr must be an array!'); + // @ts-expect-error + (() => arr2obj({}, null)).should.to.throw('arr must be an array!'); }); it('reverse()', () => { const arr = [1, '2', 'w']; - util.reverse(arr).should.eql(['w', '2', 1]); + reverse(arr).should.eql(['w', '2', 1]); }); it('reverse() - arr must be an array', () => { - (() => util.reverse({})).should.to.throw('arr must be an array!'); + // @ts-expect-error + (() => reverse({})).should.to.throw('arr must be an array!'); }); it('parseArgs()', () => { - util.parseArgs('name').should.eql({name: 1}); - util.parseArgs('name', -1).should.eql({name: -1}); - util.parseArgs('name -date').should.eql({name: 1, date: -1}); - util.parseArgs('name -date +priority').should.eql({name: 1, date: -1, priority: 1}); - util.parseArgs({name: 1}).should.eql({name: 1}); + parseArgs('name').should.eql({name: 1}); + parseArgs('name', -1).should.eql({name: -1}); + parseArgs('name -date').should.eql({name: 1, date: -1}); + parseArgs('name -date +priority').should.eql({name: 1, date: -1, priority: 1}); + parseArgs({name: 1}).should.eql({name: 1}); }); }); From 2484daf09d7fd3b45483030c80c0554b5f8329ca Mon Sep 17 00:00:00 2001 From: D-Sketon <2055272094@qq.com> Date: Thu, 4 Jul 2024 23:48:52 +0800 Subject: [PATCH 2/4] refactor: refactor types --- src/document.ts | 4 ++-- src/model.ts | 30 +++++++++++++++--------------- test/scripts/model.ts | 20 ++++++++++---------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/document.ts b/src/document.ts index dfe5479f..7130a069 100644 --- a/src/document.ts +++ b/src/document.ts @@ -69,7 +69,7 @@ abstract class Document { * * @return {object} */ - toObject(): T { + toObject(): T extends object ? T : never { const keys = Object.keys(this); const obj: Partial = {}; @@ -80,7 +80,7 @@ abstract class Document { obj[key] = isGetter(this, key) ? this[key] : cloneDeep(this[key]); } - return obj as T; + return obj as T extends object ? T : never; } /** diff --git a/src/model.ts b/src/model.ts index adbd5999..3db7b588 100644 --- a/src/model.ts +++ b/src/model.ts @@ -18,8 +18,8 @@ class Model extends EventEmitter { data: Record = {}; schema: Schema; length = 0; - Document; - Query; + Document: { new(data: T): Document }; + Query: { new(data: Document[]): Query }; _database: Database; /** @@ -154,7 +154,7 @@ class Model extends EventEmitter { const schema = this.schema; // Apply getters - const data = (data_ instanceof this.Document ? data_ : this.new(data_ as T)) as Document; + const data = (data_ instanceof this.Document ? data_ : this.new(data_)); const id = data._id; // Check ID @@ -168,7 +168,7 @@ class Model extends EventEmitter { // Apply setters const result = data.toObject(); - schema._applySetters(result as object); + schema._applySetters(result); // Pre-hooks return execHooks(schema, 'pre', 'save', data).then(data => { @@ -236,7 +236,7 @@ class Model extends EventEmitter { * @return {BluebirdPromise} * @private */ - _updateWithStack(id: string | number, stack: ((data: any) => void)[]): BluebirdPromise { + _updateWithStack(id: string | number, stack: ((data: T) => void)[]): BluebirdPromise { const schema = this.schema; const data = this.data[id]; @@ -280,7 +280,7 @@ class Model extends EventEmitter { */ updateById(id: string | number, update: object, callback?: NodeJSLikeCallback): BluebirdPromise { return BluebirdPromise.using(this._acquireWriteLock(), () => { - const stack = this.schema._parseUpdate(update as object); + const stack = this.schema._parseUpdate(update); return this._updateWithStack(id, stack); }).asCallback(callback); } @@ -294,7 +294,7 @@ class Model extends EventEmitter { * @return {BluebirdPromise} */ update(query: object, data: object, callback?: NodeJSLikeCallback): BluebirdPromise { - return (this.find(query) as Query).update(data, callback); + return this.find(query).update(data, callback); } /** @@ -315,11 +315,11 @@ class Model extends EventEmitter { (data_ as any)._id = id; // Apply getters - const data = (data_ instanceof this.Document ? data_ : this.new(data_ as T)) as Document; + const data = (data_ instanceof this.Document ? data_ : this.new(data_)); // Apply setters const result = data.toObject(); - schema._applySetters(result as object); + schema._applySetters(result); // Pre-hooks return execHooks(schema, 'pre', 'save', data).then(data => { @@ -351,8 +351,8 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {BluebirdPromise} */ - replace(query: object, data, callback?: NodeJSLikeCallback): BluebirdPromise { - return (this.find(query) as Query).replace(data, callback); + replace(query: object, data: T | Document, callback?: NodeJSLikeCallback): BluebirdPromise { + return this.find(query).replace(data, callback); } /** @@ -401,7 +401,7 @@ class Model extends EventEmitter { * @return {BluebirdPromise} */ remove(query: object, callback?: NodeJSLikeCallback): BluebirdPromise { - return (this.find(query) as Query).remove(callback); + return this.find(query).remove(callback); } /** @@ -500,7 +500,7 @@ class Model extends EventEmitter { } } - return options.lean ? arr : new this.Query(arr); + return options.lean ? arr as T[] : new this.Query(arr as Document[]); } /** @@ -881,7 +881,7 @@ class Model extends EventEmitter { } if (options.match) { - cache = (new Query(arr) as Query).find(options.match, options); + cache = new Query(arr).find(options.match, options); } else if (options.skip) { if (options.limit) { arr = arr.slice(options.skip, options.skip + options.limit); @@ -1010,7 +1010,7 @@ class Model extends EventEmitter { Model.prototype.get = Model.prototype.findById; function execHooks(schema: Schema, type: keyof Schema['hooks'], event: keyof Schema['hooks'][keyof Schema['hooks']], data: any): BluebirdPromise { - const hooks = schema.hooks[type][event] as ((data: any) => BluebirdPromise | void)[]; + const hooks = schema.hooks[type][event]; if (!hooks.length) return BluebirdPromise.resolve(data); return BluebirdPromise.each(hooks, hook => hook(data)).thenReturn(data); diff --git a/test/scripts/model.ts b/test/scripts/model.ts index abf0a70c..6b3748cd 100644 --- a/test/scripts/model.ts +++ b/test/scripts/model.ts @@ -532,7 +532,7 @@ describe('Model', () => { {age: 30}, {age: 40} ]).then(data => { - const query = User.find({age: 20}) as Query; + const query = User.find({age: 20}); query.data.should.eql(data.slice(1, 3)); return data; }).map(item => User.removeById(item._id))); @@ -544,7 +544,7 @@ describe('Model', () => { {age: 30}, {age: 40} ]).then(data => { - const query = User.find({}) as Query; + const query = User.find({}); query.data.should.eql(data); return data; }).map(item => User.removeById(item._id))); @@ -566,7 +566,7 @@ describe('Model', () => { {age: 30}, {age: 40} ]).then(data => { - const query = User.find({age: {$gte: 20}}, {limit: 2}) as Query; + const query = User.find({age: {$gte: 20}}, {limit: 2}); query.data.should.eql(data.slice(1, 3)); return data; }).map(item => User.removeById(item._id))); @@ -577,11 +577,11 @@ describe('Model', () => { {age: 30}, {age: 40} ]).then(data => { - let query = User.find({age: {$gte: 20}}, {skip: 1}) as Query; + let query = User.find({age: {$gte: 20}}, {skip: 1}); query.data.should.eql(data.slice(2)); // with limit - query = User.find({age: {$gte: 20}}, {limit: 1, skip: 1}) as Query; + query = User.find({age: {$gte: 20}}, {limit: 1, skip: 1}); query.data.should.eql(data.slice(2, 3)); return data; @@ -610,7 +610,7 @@ describe('Model', () => { ] }); - (query as Query).toArray().should.eql([data[1]]); + query.toArray().should.eql([data[1]]); return data; }).map(item => User.removeById(item._id))); @@ -627,7 +627,7 @@ describe('Model', () => { ] }); - (query as Query).toArray().should.eql(data.slice(1)); + query.toArray().should.eql(data.slice(1)); return data; }).map(item => User.removeById(item._id))); @@ -644,7 +644,7 @@ describe('Model', () => { ] }); - (query as Query).toArray().should.eql([data[0]]); + query.toArray().should.eql([data[0]]); return data; }).map(item => User.removeById(item._id))); @@ -658,7 +658,7 @@ describe('Model', () => { $not: {'name.last': 'Doe'} }); - (query as Query).toArray().should.eql(data.slice(2)); + query.toArray().should.eql(data.slice(2)); return data; }).map(item => User.removeById(item._id))); @@ -674,7 +674,7 @@ describe('Model', () => { } }); - (query as Query).toArray().should.eql(data.slice(0, 2)); + query.toArray().should.eql(data.slice(0, 2)); return data; }).map(item => User.removeById(item._id))); From 707c4f9eac8f2b96e1b08a74c57541e5f73a5c25 Mon Sep 17 00:00:00 2001 From: D-Sketon <2055272094@qq.com> Date: Sat, 6 Jul 2024 15:30:51 +0800 Subject: [PATCH 3/4] refactor: refactor types --- src/document.ts | 2 +- src/model.ts | 19 +++++++----- src/query.ts | 7 +++-- src/schema.ts | 78 +++++++++++++++++-------------------------------- src/types.ts | 33 +++++++++++++++++---- src/util.ts | 10 +++++-- 6 files changed, 79 insertions(+), 70 deletions(-) diff --git a/src/document.ts b/src/document.ts index 7130a069..26d83558 100644 --- a/src/document.ts +++ b/src/document.ts @@ -7,7 +7,7 @@ const cloneDeep = rfdc(); abstract class Document { abstract _model: Model; - _id!: string | number | undefined; + _id!: string; abstract _schema: Schema; [key : string]: any; diff --git a/src/model.ts b/src/model.ts index 3db7b588..ba924b22 100644 --- a/src/model.ts +++ b/src/model.ts @@ -11,7 +11,7 @@ import WarehouseError from './error'; import PopulationError from './error/population'; import Mutex from './mutex'; import type Database from './database'; -import type { AddSchemaTypeOptions, NodeJSLikeCallback, Options } from './types'; +import type { AddSchemaTypeOptions, NodeJSLikeCallback, Options, queryCallback } from './types'; class Model extends EventEmitter { _mutex = new Mutex(); @@ -236,7 +236,7 @@ class Model extends EventEmitter { * @return {BluebirdPromise} * @private */ - _updateWithStack(id: string | number, stack: ((data: T) => void)[]): BluebirdPromise { + _updateWithStack(id: string, stack: queryCallback[]): BluebirdPromise { const schema = this.schema; const data = this.data[id]; @@ -278,7 +278,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {BluebirdPromise} */ - updateById(id: string | number, update: object, callback?: NodeJSLikeCallback): BluebirdPromise { + updateById(id: string, update: object, callback?: NodeJSLikeCallback): BluebirdPromise { return BluebirdPromise.using(this._acquireWriteLock(), () => { const stack = this.schema._parseUpdate(update); return this._updateWithStack(id, stack); @@ -305,7 +305,7 @@ class Model extends EventEmitter { * @return {BluebirdPromise} * @private */ - _replaceById(id: string | number, data_: Document | T): BluebirdPromise { + _replaceById(id: string, data_: Document | T): BluebirdPromise { const schema = this.schema; if (!this.has(id)) { @@ -339,7 +339,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {BluebirdPromise} */ - replaceById(id: string | number, data: Document | T, callback?: NodeJSLikeCallback): BluebirdPromise { + replaceById(id: string, data: Document | T, callback?: NodeJSLikeCallback): BluebirdPromise { return BluebirdPromise.using(this._acquireWriteLock(), () => this._replaceById(id, data)).asCallback(callback); } @@ -362,7 +362,7 @@ class Model extends EventEmitter { * @return {BluebirdPromise} * @private */ - _removeById(id: string | number): BluebirdPromise { + _removeById(id: string): BluebirdPromise { const schema = this.schema; const data = this.data[id]; @@ -389,7 +389,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {BluebirdPromise} */ - removeById(id: string | number, callback?: NodeJSLikeCallback): BluebirdPromise { + removeById(id: string, callback?: NodeJSLikeCallback): BluebirdPromise { return BluebirdPromise.using(this._acquireWriteLock(), () => this._removeById(id)).asCallback(callback); } @@ -531,7 +531,10 @@ class Model extends EventEmitter { * @param {String|Number} [order] * @return {Query} */ - sort(orderby: string | object, order?: string | number | object): Query { + sort(orderby: string, order: 'desc' | number | Record): Query; + sort(orderby: string): Query; + sort(orderby: Record>): Query; + sort(orderby: string | Record>, order?: 'desc' | number | Record): Query { const sort = parseArgs(orderby, order); const fn = this.schema._execSort(sort); diff --git a/src/query.ts b/src/query.ts index dae0f58b..fcb8906f 100644 --- a/src/query.ts +++ b/src/query.ts @@ -201,14 +201,17 @@ abstract class Query { * query.sort('-date title'); * ``` * - * If the `order` equals to `-1`, `desc` or `descending`, the data will be + * If the `order` equals to `-1` or `desc`, the data will be * returned in reversed order. * * @param {String|Object} orderby * @param {String|Number} [order] * @return {Query} */ - sort(orderby: string | object, order?: string | number | object): Query { + sort(orderby: string, order: 'desc' | number | Record): Query; + sort(orderby: string): Query; + sort(orderby: Record>): Query; + sort(orderby: string | Record>, order?: 'desc' | number | Record): Query { const sort = parseArgs(orderby, order); const fn = this._schema._execSort(sort); diff --git a/src/schema.ts b/src/schema.ts index d6229d6e..7ceb42aa 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -5,45 +5,24 @@ import { getProp, setProp, delProp } from './util'; import PopulationError from './error/population'; import SchemaTypeVirtual from './types/virtual'; import { isPlainObject } from 'is-plain-object'; -import type { AddSchemaTypeLoopOptions, AddSchemaTypeOptions, Options, SchemaTypeOptions } from './types'; +import type { AddSchemaTypeLoopOptions, AddSchemaTypeOptions, AddSchemaTypeSimpleOptions, Options, queryCallback, queryFilterCallback, queryParseCallback, SchemaTypeOptions } from './types'; import type Model from './model'; - -/** - * @callback queryFilterCallback - * @param {*} data - * @return {boolean} - */ -type queryFilterCallback = (data: unknown) => boolean; - -/** - * @callback queryCallback - * @param {*} data - * @return {void} - */ -type queryCallback = (data: unknown) => void; - -/** - * @callback queryParseCallback - * @param {*} a - * @param {*} b - * @returns {*} - */ -type queryParseCallback = (a: unknown, b: unknown) => number; +import type Document from './document'; const builtinTypes = new Set(['String', 'Number', 'Boolean', 'Array', 'Object', 'Date', 'Buffer']); -const getSchemaType = (name: string, options: { type: SchemaTypeOptions; [key: string]: any } | SchemaTypeOptions) => { - const Type = (options as any).type || options; - const typeName: string = Type.name; +const getSchemaType = (name: string, options: AddSchemaTypeSimpleOptions): SchemaType => { + const Type: SchemaTypeOptions = (options as any).type || options; + const typeName = Type.name; if (builtinTypes.has(typeName)) { return new Types[typeName](name, options); } - return new Type(name, options); + return new Type(name, options as Exclude); }; -const checkHookType = (type: string) => { +const checkHookType = (type: string): void => { if (type !== 'save' && type !== 'remove') { throw new TypeError('Hook type must be `save` or `remove`!'); } @@ -57,13 +36,10 @@ const hookWrapper = (fn: (...args: any[]) => void): (...args: any[]) => Bluebird return BluebirdPromise.method(fn); }; -/** - * @param {Function[]} stack - */ -const execSortStack = (stack: ((a: unknown, b: unknown) => number)[]) => { +const execSortStack = (stack: queryParseCallback>[]): queryParseCallback> => { const len = stack.length; - return (a: any, b: any) => { + return (a: Document, b: Document) => { let result: number; for (let i = 0; i < len; i++) { @@ -75,11 +51,11 @@ const execSortStack = (stack: ((a: unknown, b: unknown) => number)[]) => { }; }; -const sortStack = (path_: SchemaType, key: string, sort: string | number) => { +const sortStack = (path_: SchemaType, key: string, sort: string | number): queryParseCallback> => { const path = path_ || new SchemaType(key); const descending = sort === 'desc' || sort === -1; - return (a: any, b: any) => { + return (a: Document, b: Document) => { const result = path.compare(getProp(a, key), getProp(b, key)); return descending && result ? result * -1 : result; }; @@ -90,7 +66,7 @@ class UpdateParser { return (data: any) => { setProp(data, key, update); }; } - static updateStackOperator(path_: SchemaType, ukey: string | number, key: string, update: any) { + static updateStackOperator(path_: SchemaType, ukey: string, key: string, update: any) { const path = path_ || new SchemaType(key); return (data: any) => { @@ -109,7 +85,7 @@ class UpdateParser { * @param {queryCallback[]} [stack] * @private */ - parseUpdate(updates: object, prefix = '', stack: queryCallback[] = []): queryCallback[] { + parseUpdate(updates: object, prefix = '', stack: queryCallback[] = []): queryCallback[] { const { paths } = this; const { updateStackOperator } = UpdateParser; const keys = Object.keys(updates); @@ -181,7 +157,7 @@ class QueryParser { queryStackOperator(qkey: string, name: string, query: any): queryFilterCallback { const path = this.paths[name] || new SchemaType(name); - return data => path[qkey](getProp(data, name), query, data); + return (data: unknown) => path[qkey](getProp(data, name), query, data); } /** @@ -190,7 +166,7 @@ class QueryParser { * @return {void} * @private */ - $and(arr: any[], stack: queryFilterCallback[]): void { + $and(arr: object[], stack: queryFilterCallback[]): void { for (let i = 0, len = arr.length; i < len; i++) { stack.push(this.execQuery(arr[i])); } @@ -201,7 +177,7 @@ class QueryParser { * @return {queryFilterCallback} * @private */ - $or(query: any[]): queryFilterCallback { + $or(query: object[]): queryFilterCallback { const stack = this.parseQueryArray(query); const len = stack.length; @@ -219,7 +195,7 @@ class QueryParser { * @return {queryFilterCallback} * @private */ - $nor(query: any[]): queryFilterCallback { + $nor(query: object[]): queryFilterCallback { const stack = this.parseQueryArray(query); const len = stack.length; @@ -237,7 +213,7 @@ class QueryParser { * @return {queryFilterCallback} * @private */ - $not(query: any): queryFilterCallback { + $not(query: object): queryFilterCallback { const stack = this.parseQuery(query); const len = stack.length; @@ -261,7 +237,7 @@ class QueryParser { * @return {queryFilterCallback} * @private */ - $where(fn: (...args: any[]) => boolean): queryFilterCallback { + $where(fn: () => boolean): queryFilterCallback { return data => Reflect.apply(fn, data, []); } @@ -272,8 +248,8 @@ class QueryParser { * @return {queryFilterCallback[]} * @private */ - parseQueryArray(arr: any[]): queryFilterCallback[] { - const stack = []; + parseQueryArray(arr: object[]): queryFilterCallback[] { + const stack: queryFilterCallback[] = []; this.$and(arr, stack); return stack; } @@ -315,7 +291,7 @@ class QueryParser { * @return {queryFilterCallback[]} * @private */ - parseQuery(queries: any): queryFilterCallback[] { + parseQuery(queries: object): queryFilterCallback[] { /** @type {queryFilterCallback[]} */ const stack: queryFilterCallback[] = []; @@ -573,7 +549,7 @@ class Schema { * @param {String} type Hook type. One of `save` or `remove`. * @param {Function} fn */ - pre(type: 'save' | 'remove', fn: (...args: any[]) => void): void { + pre(type: keyof Schema['hooks']['pre'], fn: (...args: any[]) => void): void { checkHookType(type); if (typeof fn !== 'function') throw new TypeError('Hook must be a function!'); @@ -586,7 +562,7 @@ class Schema { * @param {String} type Hook type. One of `save` or `remove`. * @param {Function} fn */ - post(type: 'save' | 'remove', fn: (...args: any[]) => void): void { + post(type: keyof Schema['hooks']['post'], fn: (...args: any[]) => void): void { checkHookType(type); if (typeof fn !== 'function') throw new TypeError('Hook must be a function!'); @@ -696,7 +672,7 @@ class Schema { * @return {queryCallback[]} * @private */ - _parseUpdate(updates: object): queryCallback[] { + _parseUpdate(updates: object): queryCallback[] { return new UpdateParser(this.paths).parseUpdate(updates); } @@ -721,7 +697,7 @@ class Schema { * @return {queryParseCallback[]} * @private */ - _parseSort(sorts: object, prefix = '', stack: queryParseCallback[] = []): queryParseCallback[] { + _parseSort(sorts: Record>, prefix = '', stack: queryParseCallback>[] = []): queryParseCallback>[] { const { paths } = this; const keys = Object.keys(sorts); @@ -747,7 +723,7 @@ class Schema { * @return {queryParseCallback} * @private */ - _execSort(sorts: object): queryParseCallback { + _execSort(sorts: Record>): queryParseCallback> { const stack = this._parseSort(sorts); return execSortStack(stack); } diff --git a/src/types.ts b/src/types.ts index 16d85d98..31a45891 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,10 @@ import type SchemaType from './schematype'; -export type NodeJSLikeCallback = (err: E, result?: R) => void +interface Constructor { + new (...args: any[]): any; +} + +export type NodeJSLikeCallback = (err: E, result?: R) => void; export interface Options { lean: boolean; @@ -12,14 +16,33 @@ export interface Options { model: string; } -export type SchemaTypeOptions = typeof SchemaType | SchemaType | ((...args: any[]) => any) +export type SchemaTypeOptions = typeof SchemaType | Constructor; -export type AddSchemaTypeSimpleOptions = SchemaTypeOptions | { type: SchemaTypeOptions; [key: string]: any }; +export type AddSchemaTypeSimpleOptions = + | SchemaTypeOptions + | { + type: SchemaTypeOptions; + required?: boolean; + default?: (() => any) | any; + [key: string]: any; + }; -export type AddSchemaTypeMixedOptions = AddSchemaTypeSimpleOptions | AddSchemaTypeSimpleOptions[]; +export type AddSchemaTypeMixedOptions = + | AddSchemaTypeSimpleOptions + | [] + | [AddSchemaTypeSimpleOptions]; export interface AddSchemaTypeLoopOptions { [key: string]: AddSchemaTypeMixedOptions | AddSchemaTypeLoopOptions; } -export type AddSchemaTypeOptions = AddSchemaTypeMixedOptions | AddSchemaTypeLoopOptions; +export type AddSchemaTypeOptions = + | AddSchemaTypeMixedOptions + | AddSchemaTypeLoopOptions + | SchemaType; + +export type queryFilterCallback = (data: unknown) => boolean; + +export type queryCallback = (data: T) => void; + +export type queryParseCallback = (a: T, b: T) => number; diff --git a/src/util.ts b/src/util.ts index aabb5188..301279d4 100644 --- a/src/util.ts +++ b/src/util.ts @@ -6,7 +6,7 @@ function _parseArgs(args: string): Record { if (typeof args !== 'string') return args; const arr = args.split(' '); - const result = {}; + const result: Record = {}; for (let i = 0, len = arr.length; i < len; i++) { const key = arr[i]; @@ -173,8 +173,12 @@ export function reverse(arr: T[]): T[] { return arr; } -export function parseArgs(orderby: string | object, order?: string | number | object): Record { - let result; +export function parseArgs>(orderby: B, order: O): { [key in typeof orderby]: typeof order }; +export function parseArgs(orderby: B): Record; +export function parseArgs, O>(orderby: B): B; +export function parseArgs>, O extends number | string | Record>(orderby: B, order?: O): Record; +export function parseArgs>, O extends number | string | Record>(orderby: B, order?: O) { + let result: Record>; if (order) { result = {}; From 7b13325772058a8d58e0d837cb950f1498c90166 Mon Sep 17 00:00:00 2001 From: D-Sketon <2055272094@qq.com> Date: Sat, 6 Jul 2024 21:31:36 +0800 Subject: [PATCH 4/4] refactor: refactor types --- src/model.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/model.ts b/src/model.ts index ba924b22..9e339437 100644 --- a/src/model.ts +++ b/src/model.ts @@ -21,6 +21,7 @@ class Model extends EventEmitter { Document: { new(data: T): Document }; Query: { new(data: Document[]): Query }; _database: Database; + [key : string]: any; /** * Model constructor.