Skip to content

Commit

Permalink
feat(types::Ref): update to transparently use "DocumentType"
Browse files Browse the repository at this point in the history
fixes #730
fixes #772

BREAKING CHANGE:
"Ref" now transparently uses "DocumentType", which could lead ot breaking changes.
"isDocumentType" and "isRefType" now narrow out the type that is tested, which could be a breaking change.
  • Loading branch information
hasezoey committed Nov 26, 2022
1 parent a7094a4 commit 4b3520e
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 24 deletions.
25 changes: 11 additions & 14 deletions src/typeguards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ import type { DocumentType, Ref, RefType } from './types';
* Check if the given document is populated
* @param doc The Ref with uncertain type
*/
export function isDocument<T, S extends RefType>(
doc: Ref<T, S>
// handle type case of T being "DocumentType" already and being a OR of other types (like for count hooks)
// i am not a typescript wizard, so i dont know how to handle this better, this will need to be updated for #730 and #587
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore somehow the following errors in typescript 4.9, but can be disabled with a directive
): doc is T extends DocumentType<infer T1, infer T2> ? DocumentType<T1, T2> : T extends object ? DocumentType<T> : never {
export function isDocument<T, S extends RefType>(doc: Ref<T, S> | null | undefined): doc is DocumentType<T> {
return doc instanceof mongoose.Model;
}

Expand All @@ -23,15 +17,15 @@ export function isDocument<T, S extends RefType>(
* @param docs The Array of Refs with uncertain type
*/
export function isDocumentArray<T, S extends RefType>(
docs: mongoose.Types.Array<Ref<T, S>> | undefined
docs: mongoose.Types.Array<Ref<T, S>> | null | undefined
): docs is mongoose.Types.Array<DocumentType<NonNullable<T>>>;
/**
* Check if the given array is fully populated
* Only returns "true" if all members in the array are populated
* @param docs The Array of Refs with uncertain type
*/
export function isDocumentArray<T, S extends RefType>(docs: Ref<T, S>[] | undefined): docs is DocumentType<NonNullable<T>>[];
export function isDocumentArray(docs: Ref<any, any>[] | undefined): unknown {
export function isDocumentArray<T, S extends RefType>(docs: Ref<T, S>[] | null | undefined): docs is DocumentType<NonNullable<T>>[];
export function isDocumentArray(docs: Ref<any, any>[] | null | undefined): unknown {
// its "any" & "unknown" because this is not listed as an overload
return Array.isArray(docs) && docs.every((v) => isDocument(v));
}
Expand All @@ -43,7 +37,7 @@ type AllowedRefTypes = typeof String | typeof Number | typeof Buffer | typeof mo
* @param doc The Ref with uncretain type
* @param refType The Expected Reference Type (this is required because this type is only known at compile time, not at runtime)
*/
export function isRefType<T, S extends RefType>(doc: Ref<T, S> | undefined, refType: AllowedRefTypes): doc is NonNullable<S> {
export function isRefType<T, S extends RefType>(doc: Ref<T, S> | null | undefined, refType: AllowedRefTypes): doc is NonNullable<S> {
logger.info('isRefType:', refType);

if (isNullOrUndefined(doc) || isDocument(doc)) {
Expand Down Expand Up @@ -74,7 +68,7 @@ export function isRefType<T, S extends RefType>(doc: Ref<T, S> | undefined, refT
* @param refType The Expected Reference Type (this is required because this type is only known at compile time, not at runtime)
*/
export function isRefTypeArray<T, S extends RefType>(
docs: mongoose.Types.Array<Ref<T, S>> | undefined,
docs: mongoose.Types.Array<Ref<T, S>> | null | undefined,
refType: AllowedRefTypes
): docs is mongoose.Types.Array<NonNullable<S>>;
/**
Expand All @@ -83,8 +77,11 @@ export function isRefTypeArray<T, S extends RefType>(
* @param docs The Ref with uncretain type
* @param refType The Expected Reference Type (this is required because this type is only known at compile time, not at runtime)
*/
export function isRefTypeArray<T, S extends RefType>(docs: Ref<T, S>[] | undefined, refType: AllowedRefTypes): docs is NonNullable<S>[];
export function isRefTypeArray(docs: Ref<any, any>[] | undefined, refType: AllowedRefTypes): unknown {
export function isRefTypeArray<T, S extends RefType>(
docs: Ref<T, S>[] | null | undefined,
refType: AllowedRefTypes
): docs is NonNullable<S>[];
export function isRefTypeArray(docs: Ref<any, any>[] | null | undefined, refType: AllowedRefTypes): unknown {
// its "any" & "unknown" because this is not listed as an overload
return Array.isArray(docs) && docs.every((v) => isRefType(v, refType));
}
Expand Down
8 changes: 4 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,10 +392,10 @@ export type RefType = mongoose.RefType;
*/
export type Ref<
PopulatedType,
RawId extends mongoose.RefType =
| (PopulatedType extends { _id?: mongoose.RefType } ? NonNullable<PopulatedType['_id']> : mongoose.Types.ObjectId)
| undefined
> = mongoose.PopulatedDoc<PopulatedType, RawId>;
RawId extends mongoose.RefType = PopulatedType extends { _id?: mongoose.RefType }
? NonNullable<PopulatedType['_id']>
: mongoose.Types.ObjectId
> = mongoose.PopulatedDoc<DocumentType<PopulatedType>, RawId>;

/**
* A Function type for a function that doesn't have any arguments and doesn't return anything
Expand Down
2 changes: 1 addition & 1 deletion test/tests/typeguards.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ describe('isRefType / isRefTypeArray', () => {

it('should return "false" if all other fail', async () => {
const obj = { hello: String };
expect(isDocument(obj)).toStrictEqual(false);
expect(isDocument(obj as any)).toStrictEqual(false);
expect(
// @ts-expect-error "Array" is not a supported type for param2, but is used to test the fallback-case
isRefType(obj, Array)
Expand Down
22 changes: 17 additions & 5 deletions test/tests/types/basicTypegoose.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expectType, expectAssignable } from 'tsd-lite';
import { expectType, expectAssignable, expectError } from 'tsd-lite';
import * as typegoose from '../../../src/typegoose';
import { isDocument, isRefType } from '../../../src/typegoose';
import { BeAnObject, IObjectWithTypegooseFunction } from '../../../src/types';

// decorators return type
Expand Down Expand Up @@ -71,13 +72,13 @@ async function typeguards() {
if (typegoose.isDocument(someNewDoc.refObjectId)) {
expectAssignable<typegoose.DocumentType<TypeguardsClass>>(someNewDoc.refObjectId);
} else {
expectType<typegoose.Ref<TypeguardsClass, typegoose.mongoose.Types.ObjectId> | undefined>(someNewDoc.refObjectId);
expectType<typegoose.mongoose.Types.ObjectId | undefined>(someNewDoc.refObjectId);
}

if (typegoose.isDocument(someNewDoc.refString)) {
expectAssignable<typegoose.DocumentType<TypeguardsClass>>(someNewDoc.refString);
} else {
expectType<typegoose.Ref<TypeguardsClass, string> | undefined>(someNewDoc.refString);
expectType<string | undefined>(someNewDoc.refString);
}
}

Expand All @@ -104,13 +105,13 @@ async function typeguards() {
if (typegoose.isRefType(someNewDoc.refObjectId, typegoose.mongoose.Types.ObjectId)) {
expectType<typegoose.mongoose.Types.ObjectId>(someNewDoc.refObjectId);
} else {
expectType<TypeguardsClass | undefined>(someNewDoc.refObjectId);
expectType<typegoose.DocumentType<TypeguardsClass> | undefined>(someNewDoc.refObjectId);
}

if (typegoose.isRefType(someNewDoc.refString, String)) {
expectType<string>(someNewDoc.refString);
} else {
expectType<TypeguardsClass | undefined>(someNewDoc.refString);
expectType<typegoose.DocumentType<TypeguardsClass> | undefined>(someNewDoc.refString);
}
}

Expand Down Expand Up @@ -150,6 +151,17 @@ async function typeguards() {
// same here
}
}

// test primitives
isDocument(null);
isDocument(undefined);
isDocument('string');
isRefType(null, String);
isRefType(undefined, String);
isRefType('string', String);

// test errors
expectError<Parameters<typeof isDocument>[0]>({});
}

typeguards();
Expand Down

0 comments on commit 4b3520e

Please sign in to comment.