Skip to content

Commit

Permalink
Merge pull request #1 from mohammad0-0ahmad-forks/_12007
Browse files Browse the repository at this point in the history
  • Loading branch information
iammola authored Jun 30, 2022
2 parents 27614de + 1c82c05 commit c64f97b
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 57 deletions.
15 changes: 9 additions & 6 deletions test/types/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
Schema,
Document,
SchemaDefinition,
SchemaDefinitionProperty,
SchemaTypeOptions,
Model,
Types,
Expand Down Expand Up @@ -383,11 +382,13 @@ export function autoTypedSchema() {
customSchema?: Int8;
map1?: Map<string, string>;
map2?: Map<string, number>;
array1?: string[];
array2?: any[];
array3?: any[];
array4?: any[];
array5?: any[];
array1: string[];
array2: any[];
array3: any[];
array4: any[];
array5: any[];
array6: string[];
array7?: string[];
decimal1?: Types.Decimal128;
decimal2?: Types.Decimal128;
decimal3?: Types.Decimal128;
Expand Down Expand Up @@ -428,6 +429,8 @@ export function autoTypedSchema() {
array3: [Schema.Types.Mixed],
array4: [{}],
array5: [],
array6: { type: [String] },
array7: { type: [String], default: undefined },
decimal1: Schema.Types.Decimal128,
decimal2: 'Decimal128',
decimal3: 'decimal128'
Expand Down
100 changes: 49 additions & 51 deletions types/inferschematype.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Schema, Types, InferSchemaType, ObtainDocumentType, SchemaDefinitionProperty, SchemaType, SchemaTypeOptions, TypeKeyBaseType } from 'mongoose';
import { Schema, InferSchemaType, SchemaType, SchemaTypeOptions, TypeKeyBaseType, Types } from 'mongoose';

declare module 'mongoose' {
/**
* @summary Obtains document schema type.
* @description Obtains document schema type from document Definition OR returns enforced schema type if it's provided.
* @param {DocDefinition} DocDefinition A generic equals to the type of document definition "provided in as first parameter in Schema constructor".
* @param {EnforcedDocType} EnforcedDocType A generic type enforced by user "provided before schema constructor".
* @param {TypeKey} TypeKey A generic of literal string type.
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
*/
type ObtainDocumentType<DocDefinition, EnforcedDocType = any, TypeKey extends TypeKeyBaseType = DefaultTypeKey> =
IsItRecordAndNotAny<EnforcedDocType> extends true ? EnforcedDocType : {
[K in keyof (RequiredPaths<DocDefinition, TypeKey> &
OptionalPaths<DocDefinition, TypeKey>)]: ObtainDocumentPathType<DocDefinition[K], TypeKey>;
};
type ObtainDocumentType<DocDefinition, EnforcedDocType = any, TypeKey extends TypeKeyBaseType = DefaultTypeKey> =
IsItRecordAndNotAny<EnforcedDocType> extends true ? EnforcedDocType : {
[K in keyof (RequiredPaths<DocDefinition, TypeKey> &
OptionalPaths<DocDefinition, TypeKey>)]: ObtainDocumentPathType<DocDefinition[K], TypeKey>;
};

/**
* @summary Obtains document schema type from Schema instance.
Expand All @@ -23,7 +23,7 @@ declare module 'mongoose' {
* // result
* type UserType = {userName?: string}
*/
type InferSchemaType<SchemaType> = ObtainSchemaGeneric<SchemaType, 'DocType'>;
type InferSchemaType<SchemaType> = ObtainSchemaGeneric<SchemaType, 'DocType'> ;

/**
* @summary Obtains schema Generic type by using generic alias.
Expand Down Expand Up @@ -64,10 +64,18 @@ type IfEquals<T, U, Y = true, N = false> =
(<G>() => G extends U ? 1 : 0) ? Y : N;

/**
* @summary Required path base type.
* @description It helps to check whereas if a path is required OR optional.
* @summary Checks if a document path is required or optional.
* @param {P} P Document path.
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
*/
type RequiredPathBaseType = { required: true | [true, string | undefined] };
type IsPathRequired<P, TypeKey extends TypeKeyBaseType> =
P extends { required: true | [true, string | undefined] } | ArrayConstructor | any[]
? true
: P extends (Record<TypeKey, ArrayConstructor | any[]>)
? P extends { default: undefined }
? false
: true
: false;

/**
* @summary Path base type defined by using TypeKey
Expand All @@ -76,31 +84,20 @@ type RequiredPathBaseType = { required: true | [true, string | undefined] };
*/
type PathWithTypePropertyBaseType<TypeKey extends TypeKeyBaseType> = { [k in TypeKey]: any };

/**
* @summary
* @description Gets the type of a path defined by using TypeKey or directly specify type
* @param {PathValueType} PathValueType The schema path
* @param {TypeKey} TypeKey A literal string refers to path type property key.
*/
type PathTypeDefinition<PathValueType, TypeKey extends TypeKeyBaseType> = PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? PathValueType[TypeKey] : PathValueType;

/**
* @summary A Utility to obtain schema's required path keys.
* @param {T} T A generic refers to document definition.
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
* @returns required paths keys of document definition.
*/
type RequiredPathKeys<T, TypeKey extends TypeKeyBaseType> = {
[K in keyof T]: T[K] extends RequiredPathBaseType ? IfEquals<T[K], any, never, K>
// Check if the path's type is an array
: PathTypeDefinition<T[K], TypeKey> extends any[]
// If the path is an array and is defaulted to undefined, then we remove it from the required keys
? T[K] extends { default: undefined } ? never : K
: never;
[K in keyof T]: IsPathRequired<T[K], TypeKey> extends true ? IfEquals<T[K], any, never, K> : never;
}[keyof T];

/**
* @summary A Utility to obtain schema's required paths.
* @param {T} T A generic refers to document definition.
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
* @returns a record contains required paths with the corresponding type.
*/
type RequiredPaths<T, TypeKey extends TypeKeyBaseType> = {
Expand All @@ -110,20 +107,17 @@ type RequiredPaths<T, TypeKey extends TypeKeyBaseType> = {
/**
* @summary A Utility to obtain schema's optional path keys.
* @param {T} T A generic refers to document definition.
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
* @returns optional paths keys of document definition.
*/
type OptionalPathKeys<T, TypeKey extends TypeKeyBaseType> = {
[K in keyof T]: T[K] extends RequiredPathBaseType ? never
// Check if the path's type is an array
: PathTypeDefinition<T[K], TypeKey> extends any[]
// If the path is an array and is defaulted to undefined, then we add it to the optional keys
? T[K] extends { default: undefined } ? K : never
: K;
[K in keyof T]: IsPathRequired<T[K], TypeKey> extends true ? never : K;
}[keyof T];

/**
* @summary A Utility to obtain schema's optional paths.
* @param {T} T A generic refers to document definition.
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
* @returns a record contains optional paths with the corresponding type.
*/
type OptionalPaths<T, TypeKey extends TypeKeyBaseType> = {
Expand All @@ -136,11 +130,12 @@ type OptionalPaths<T, TypeKey extends TypeKeyBaseType> = {
* @param {PathValueType} PathValueType Document definition path type.
* @param {TypeKey} TypeKey A generic refers to document definition.
*/
type ObtainDocumentPathType<PathValueType, TypeKey extends TypeKeyBaseType> = ResolvePathType<
TypeKey,
PathTypeDefinition<PathValueType, TypeKey>,
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? Omit<PathValueType, TypeKey> : {}
>;
type ObtainDocumentPathType<PathValueType, TypeKey extends TypeKeyBaseType> = PathValueType extends Schema<any>
? InferSchemaType<PathValueType>
: ResolvePathType<
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? PathValueType[TypeKey] : PathValueType,
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? Omit<PathValueType, TypeKey> : {}
>;

/**
* @param {T} T A generic refers to string path enums.
Expand All @@ -154,18 +149,21 @@ type PathEnumOrString<T extends SchemaTypeOptions<string>['enum']> = T extends (
* @param {Options} Options Document definition path options except path type.
* @returns Number, "Number" or "number" will be resolved to string type.
*/
type ResolvePathType<TypeKey extends TypeKeyBaseType, PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}> =
PathValueType extends Schema<any> ? InferSchemaType<PathValueType> :
PathValueType extends (infer Item)[] ? IfEquals<Item, never, any, ResolvePathType<TypeKey, Item>>[] :
PathValueType extends { [K: string]: SchemaDefinitionProperty<any> } ? ObtainDocumentType<PathValueType, any, TypeKey> :
PathValueType extends StringConstructor | 'string' | 'String' | typeof Schema.Types.String ? PathEnumOrString<Options['enum']> :
PathValueType extends NumberConstructor | 'number' | 'Number' | typeof Schema.Types.Number ? number :
PathValueType extends DateConstructor | 'date' | 'Date' | typeof Schema.Types.Date ? Date :
PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer :
PathValueType extends BooleanConstructor | 'boolean' | 'Boolean' | typeof Schema.Types.Boolean ? boolean :
PathValueType extends 'objectId' | 'ObjectId' | typeof Schema.Types.ObjectId ? Types.ObjectId :
PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 :
PathValueType extends MapConstructor ? Map<string, ResolvePathType<TypeKey, Options['of']>> :
PathValueType extends ArrayConstructor ? any[] :
PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
any;
type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}> =
PathValueType extends Schema ? InferSchemaType<PathValueType> :
PathValueType extends (infer Item)[] ? IfEquals<Item, never, any, ResolvePathType<Item>>[] :
PathValueType extends StringConstructor | 'string' | 'String' | typeof Schema.Types.String ? PathEnumOrString<Options['enum']> :
PathValueType extends NumberConstructor | 'number' | 'Number' | typeof Schema.Types.Number ? number :
PathValueType extends DateConstructor | 'date' | 'Date' | typeof Schema.Types.Date ? Date :
PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer :
PathValueType extends BooleanConstructor | 'boolean' | 'Boolean' | typeof Schema.Types.Boolean ? boolean :
PathValueType extends 'objectId' | 'ObjectId' | typeof Schema.Types.ObjectId ? Types.ObjectId :
PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 :
PathValueType extends MapConstructor ? Map<string, ResolvePathType<Options['of']>> :
PathValueType extends ArrayConstructor ? any[] :
PathValueType extends typeof Schema.Types.Mixed ? any:
IfEquals<PathValueType, ObjectConstructor> extends true ? any:
IfEquals<PathValueType, {}> extends true ? any:
PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
PathValueType extends {} ? PathValueType :
unknown;

0 comments on commit c64f97b

Please sign in to comment.