diff --git a/src/document.ts b/src/document.ts index 7130a06..26d8355 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 3db7b58..ba924b2 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 dae0f58..fcb8906 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 d6229d6..7ceb42a 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 16d85d9..31a4589 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 aabb518..301279d 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 = {};