Typing mongoose findOne query when projection object is provided

Viewed 983

FIRST OF ALL it's a typescript only question. I don't care about the js part.

My final goal is to type a simple mongoose find with a projection object.

let's say movie as 3 fields :

  • name -> string,
  • email -> string
  • isGood -> boolean
Movie.findOne({ year: { $gte: 1980, $lte: 1989 }, {isGood: -1}, function(err, movie) {
  // Here I want ts compiler to understand that returned object does not have the isGood property
});

I want my returned object to be typed like this (without the isGood property since we omit it in the projection object.

{
  name: string,
  email: string
}

Right now I have this code :

type Diff<T, U> = T extends U ? never : T;  // Remove types from T that are assignable to U

interface Test {
    blob: number;
    pop: number;
}

interface UserProfile {
    id: string
    email: string
    image: string | null
    isAdmin: boolean
    username: string
    reviews: string[],
    test: Test
}

type ExcludedTypes<T, U> = {
    [K in Exclude<keyof T, keyof U>]: T[K]
}

type PartialFrom<T> = {
    [P in keyof T]?: number | PartialFrom<T[P]>;
}

const excludeFn = <K extends Record<string, any>, T extends PartialFrom<K>>(user: K, projection: T): ExcludedTypes<K, T> => {
    const newUser: K = {
        ...user
    };

    Object.keys(projection).forEach((key) => {
        delete newUser[key];
    })

    return user;
}

const full: UserProfile = {
    id: "reareaz",
    email: "rezrza",
    image: null,
    isAdmin: false,
    username: "rezaraz",
    reviews: ["test"],
    test: {
        blob: 1221,
        pop: 122,
    }
}

const projection = {
    email: -1,
    image: -1,
    reviews: -1,
}

const filteredObject = excludeFn(full, projection);

interface PartialUserProfile {
    id: string
    isAdmin: boolean
    username: string
}

// this is just used to test that filteredObject is properly typed 
const ppp = (a: PartialUserProfile): void => { }

// it should fail because email is not in filtered object anymore
ppp(filteredObject);

So the goal here is to create a types function that take a full object as first argument, a partial object and return fields of full object that are NOT in partial object.

My exemple above work only on first level properties :

It work :

const full = {
  image: 'string',
  isFalse: true
}

const projection = {
 image: -1
}

// will not have image property
const filteredObject = excludeFn(full, projection);

Won't work :

const full = {
  image: 'string',
  pasta: {
    isGood: true,
    brand: 'mypastabrand'
  }
}

const projection = {
 pasta: {
   isGood: -1
 }
}

const filteredObject = excludeFn(full, projection);

This won't work because my type :

type ExcludedTypes<T, U> = {
    [K in Exclude<keyof T, keyof U>]: T[K]
}

Does not go more than one level deep and that's where I'm stuck

0 Answers
Related