Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create separate generic for schema definition #13856

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion test/types/docArray.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ async function gh10293() {
});

const TestModel = model('gh10293TestModel', testSchema);
const doc = new TestModel();
expectType<string>(doc.name);
expectType<string[][]>(doc.arrayOfArray);

testSchema.methods.getArrayOfArray = function(this: InstanceType<typeof TestModel>): string[][] { // <-- function to return Array of Array
const test = this.toObject();
Expand Down Expand Up @@ -45,7 +48,7 @@ function gh13087() {
required: true,
type: [Number] // [longitude, latitude]
}
},
} as const,
{ _id: false }
);

Expand Down
7 changes: 7 additions & 0 deletions test/types/document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ async function gh11960() {

type ParentModelType = Model<Parent, {}, {}, {}, ParentDocument>;

const schemaDefinition = {
username: { type: String },
map: { type: Map, of: String },
nested: { type: NestedSchema },
nestedArray: [{ type: NestedSchema }]
} as const;
const ParentSchema = new Schema<
Parent,
ParentModelType,
Expand All @@ -238,6 +244,7 @@ async function gh11960() {
{},
{},
DefaultSchemaOptions,
typeof schemaDefinition,
Parent,
ParentDocument
>({
Expand Down
4 changes: 3 additions & 1 deletion test/types/querycursor.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Schema, model, Model, Types } from 'mongoose';
import { expectType } from 'tsd';

const schema = new Schema({ name: { type: 'String' } });
const schema = new Schema({ name: { type: 'String' as const } });

const Test = model('Test', schema);

Expand All @@ -20,3 +20,5 @@ Test.find().cursor().
expectType<number>(i);
}).
then(() => console.log('Done!'));

expectType<{ name: { type: 'String' } }>(schema.obj);
44 changes: 34 additions & 10 deletions test/types/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ export function autoTypedSchema() {
decimal1: Schema.Types.Decimal128,
decimal2: 'Decimal128',
decimal3: 'decimal128'
});
} as const);

type InferredTestSchemaType = InferSchemaType<typeof TestSchema>;

Expand All @@ -483,7 +483,7 @@ export function autoTypedSchema() {
const AutoTypedSchema = new Schema({
userName: {
type: String,
required: [true, 'userName is required']
required: [true, 'userName is required'] as [true, string]
},
description: String,
nested: new Schema({
Expand Down Expand Up @@ -519,7 +519,7 @@ export function autoTypedSchema() {
})
]
}
}, {
} as const, {
statics: {
staticFn() {
expectType<Model<InferSchemaType<typeof AutoTypedSchema>>>(this);
Expand Down Expand Up @@ -558,8 +558,8 @@ export type AutoTypedSchemaType = {
date: Date;
messages?: number;
}>
}
, statics: {
},
statics: {
staticFn: () => 'Returned from staticFn'
},
methods: {
Expand Down Expand Up @@ -596,17 +596,24 @@ function gh11828() {
}
};

new Schema<IUser>({
const schema = new Schema<IUser>({
name: { type: String, default: () => 'Hafez' },
age: { type: Number, default: () => 27 },
bornAt: { type: Date, default: () => new Date() },
isActive: {
type: Boolean,
default(): boolean {
default(this: HydratedDocument<IUser>): boolean {
return this.name === 'Hafez';
}
}
});

const UserModel = model<IUser>('User', schema);
const doc = new UserModel();
expectType<string>(doc.name);
expectType<number>(doc.age);
expectType<Date>(doc.bornAt);
expectType<boolean>(doc.isActive);
}

function gh11997() {
Expand Down Expand Up @@ -760,7 +767,7 @@ function pluginOptions() {
}

const schema = new Schema({});
expectType<Schema<any>>(schema.plugin(pluginFunction)); // test that chaining would be possible
expectAssignable<Schema<any>>(schema.plugin(pluginFunction)); // test that chaining would be possible

// could not add strict tests that the parameters are inferred correctly, because i dont know how this would be done in tsd

Expand Down Expand Up @@ -1007,7 +1014,7 @@ function gh12869() {

const dbExample = new Schema(
{
active: { type: String, enum: ['foo', 'bar'], required: true }
active: { type: String, enum: ['foo', 'bar'] as ['foo', 'bar'], required: true }
vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved
}
);

Expand All @@ -1031,7 +1038,7 @@ function gh12882() {
const arrNum = new Schema({
fooArray: {
type: [{
type: 'Number',
type: 'Number' as const,
required: true
}],
required: true
Expand Down Expand Up @@ -1154,6 +1161,23 @@ function gh13514() {
const str: string = doc.email;
}

function defaultFn() {
const schema = new Schema({
name: String,
email: {
type: String,
// Requires `this: any` even in 7.0.
default(this: any) {
return this.name + '@test.com';
}
}
});
const Test = model('Test', schema);

const doc = new Test({ name: 'john' });
const str: string = doc.email;
}

function gh13633() {
const schema = new Schema({ name: String });

Expand Down
5 changes: 3 additions & 2 deletions test/types/virtuals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,15 @@ async function autoTypedVirtuals() {
const testSchema = new Schema({
email: {
type: String,
required: [true, 'email is required']
required: [true, 'email is required'] as [true, string]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are those array castings really required instead of as const?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately yes. I think that's more of a TypeScript 5 issue though, similar to #13514.

}
}, {
virtuals: {
domain: {
get() {
expectType<Document<any, any, { email: string }> & AutoTypedSchemaType>(this);
return this.email.slice(this.email.indexOf('@') + 1);
const email = this.email;
return email.slice(email.indexOf('@') + 1);
vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved
},
set() {
expectType<Document<any, any, AutoTypedSchemaType> & AutoTypedSchemaType>(this);
Expand Down
29 changes: 15 additions & 14 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,16 @@ declare module 'mongoose' {
collection?: string,
options?: CompileModelOptions
): Model<
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>,
ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'>,
HydratedDocument<
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'> & ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>
>,
TSchema
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>,
ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'>,
HydratedDocument<
InferSchemaType<TSchema>,
ObtainSchemaGeneric<TSchema, 'TVirtuals'> & ObtainSchemaGeneric<TSchema, 'TInstanceMethods'>,
ObtainSchemaGeneric<TSchema, 'TQueryHelpers'>
>,
TSchema
> & ObtainSchemaGeneric<TSchema, 'TStaticMethods'>;

export function model<T>(name: string, schema?: Schema<T, any, any> | Schema<T & Document, any, any>, collection?: string, options?: CompileModelOptions): Model<T>;
Expand Down Expand Up @@ -223,11 +223,12 @@ declare module 'mongoose' {
TVirtuals = {},
TStaticMethods = {},
TSchemaOptions = DefaultSchemaOptions,
TSchemaDefinition = any,
DocType extends ApplySchemaOptions<
ObtainDocumentType<DocType, EnforcedDocType, ResolveSchemaOptions<TSchemaOptions>>,
ObtainDocumentType<TSchemaDefinition, EnforcedDocType, ResolveSchemaOptions<TSchemaOptions>>,
ResolveSchemaOptions<TSchemaOptions>
> = ApplySchemaOptions<
ObtainDocumentType<any, EnforcedDocType, ResolveSchemaOptions<TSchemaOptions>>,
ObtainDocumentType<TSchemaDefinition, EnforcedDocType, ResolveSchemaOptions<TSchemaOptions>>,
ResolveSchemaOptions<TSchemaOptions>
>,
THydratedDocumentType = HydratedDocument<DocType, TVirtuals & TInstanceMethods>
Expand All @@ -236,7 +237,7 @@ declare module 'mongoose' {
/**
* Create a new schema
*/
constructor(definition?: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | DocType, options?: SchemaOptions<DocType, TInstanceMethods, TQueryHelpers, TStaticMethods, TVirtuals, THydratedDocumentType> | ResolveSchemaOptions<TSchemaOptions>);
constructor(definition?: TSchemaDefinition, options?: SchemaOptions<DocType, TInstanceMethods, TQueryHelpers, TStaticMethods, TVirtuals, THydratedDocumentType> | ResolveSchemaOptions<TSchemaOptions>);

/** Adds key path / schema type pairs to this schema. */
add(obj: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | Schema, prefix?: string): this;
Expand Down Expand Up @@ -300,7 +301,7 @@ declare module 'mongoose' {
methods: { [F in keyof TInstanceMethods]: TInstanceMethods[F] } & AnyObject;

/** The original object passed to the schema constructor */
obj: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>>;
obj: TSchemaDefinition;

/** Gets/sets schema paths. */
path<ResultType extends SchemaType = SchemaType<any, THydratedDocumentType>>(path: string): ResultType;
Expand Down
11 changes: 6 additions & 5 deletions types/inferschematype.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ declare module 'mongoose' {
* @param {TSchema} TSchema A generic of schema type instance.
* @param {alias} alias Targeted generic alias.
*/
type ObtainSchemaGeneric<TSchema, alias extends 'EnforcedDocType' | 'M' | 'TInstanceMethods' | 'TQueryHelpers' | 'TVirtuals' | 'TStaticMethods' | 'TSchemaOptions' | 'DocType'> =
TSchema extends Schema<infer EnforcedDocType, infer M, infer TInstanceMethods, infer TQueryHelpers, infer TVirtuals, infer TStaticMethods, infer TSchemaOptions, infer DocType>
type ObtainSchemaGeneric<TSchema, alias extends 'EnforcedDocType' | 'M' | 'TInstanceMethods' | 'TQueryHelpers' | 'TVirtuals' | 'TStaticMethods' | 'TSchemaOptions' | 'TSchemaDefinition' | 'DocType'> =
TSchema extends Schema<infer EnforcedDocType, infer M, infer TInstanceMethods, infer TQueryHelpers, infer TVirtuals, infer TStaticMethods, infer TSchemaOptions, infer TSchemaDefinition, infer DocType>
? {
EnforcedDocType: EnforcedDocType;
M: M;
Expand All @@ -56,6 +56,7 @@ declare module 'mongoose' {
TVirtuals: TVirtuals;
TStaticMethods: TStaticMethods;
TSchemaOptions: TSchemaOptions;
TSchemaDefinition: TSchemaDefinition;
DocType: DocType;
}[alias]
: unknown;
Expand All @@ -76,7 +77,7 @@ declare module 'mongoose' {

type IsPathDefaultUndefined<PathType> = PathType extends { default: undefined } ?
true :
PathType extends { default: (...args: any[]) => undefined } ?
PathType extends { default: (this: any, ...args: any[]) => undefined } ?
true :
false;

Expand All @@ -86,13 +87,13 @@ type IsPathDefaultUndefined<PathType> = PathType extends { default: undefined }
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
*/
type IsPathRequired<P, TypeKey extends string = DefaultTypeKey> =
P extends { required: true | [true, string | undefined] | { isRequired: true } } | ArrayConstructor | any[]
P extends { required: true | [true, string | undefined] | { isRequired: true } } | ArrayConstructor | any[] | ReadonlyArray<any>
? true
: P extends { required: boolean }
? P extends { required: false }
? false
: true
: P extends (Record<TypeKey, ArrayConstructor | any[]>)
: P extends (Record<TypeKey, ArrayConstructor | any[] | ReadonlyArray<any>>)
? IsPathDefaultUndefined<P> extends true
? false
: true
Expand Down