typescriptmongoosetypescript-genericstypegoose

Extracting raw Typegoose document's type with infer gives unknown


I've created a codesandbox for easier trial. But below are also the parts that are more related to my question.

This is the type that is expected to extract the raw Mongoose document's type (It doesn't.):

// ⭐ class Document<T = any, TQueryHelpers = any, DocType = any>
type ObtainRawDocType<T> = T extends mongoose.Document<
  infer A,
  infer B,
  infer C
>
  ? C // <--- ⭐ Must return the raw document's type.
  : never;

This is where the above generic is used (The return type is not right on purpose, in order to test the above generic.):

export function clearFields<
  T extends mongoose.Document,
  U extends PropertyKey[]
>(dataObj: T, privateFields: U): ObtainRawDocType<T> {

However, this cc1's type is detected to be unknown:

const cc1 = clearFields(c1, ["birthdate"]);

Solution

  • this question was also asked on the typegoose discord, where we came to the conclusion that the resulting unknown is because typescript discards the original type if it has been AND'd with a Omit type and is not structurally used anywhere.

    typescript playground example of the problem

    the answer is basically to augment the mongoose.Document type to structurally use the generic that is wanted (in this case the currently named DocType generic (mongoose 7.3.1)

    // NodeJS: 19.9.0
    // MongoDB: 5.0 (Docker)
    // Typescript 4.9.5
    import { getModelForClass, prop } from '@typegoose/typegoose'; // @typegoose/typegoose@11.3.0
    import * as mongoose from 'mongoose'; // mongoose@7.3.1
    
    declare module 'mongoose' {
      interface Document<T = any, TQueryHelpers = any, DocType = any> {
        __DOCTYPE: DocType; // some kind of property to structurally use the generic
      }
    }
    
    type ObtainRawDocType<T> = T extends mongoose.Document<infer A, infer B, infer C> ? C : never;
    
    function clearFields<T extends mongoose.Document, U extends PropertyKey[]>(dataObj: T, privateFields: U): ObtainRawDocType<T> {
      return undefined as any; // Temporary
    }
    
    class User {
      @prop()
      public username?: string;
    }
    
    const UserModel = getModelForClass(User);
    
    const c1 = new UserModel({});
    
    const cc1 = clearFields(c1, ['birthdate']);
    

    Notes: