Search code examples
typescriptinterfacelodash

Referencing another interface in a interface when using lodash groupBy method in typescript


I have a database model interface User. I want to fetch all users and store them in a key-value pair with groupBy() from lodash like this:

const users: {[_id: string]: User} = _.groupBy(await db.Users.find(), '_id');
// users = {'x': {_id: 'x', name: 'Rifat'}, 'y': {_id: 'y', name: 'Reza'}}

This is giving me this error Type 'Dictionary<(Document<unknown, any, User> & Omit<User & Required<{ _id: string; }>, never>)[]>' is not assignable to type '{ [_id: string]: User; }'. 'string' index signatures are incompatible.

Eventually I want to be able to do something like this:

const user = users['x']; // which should treat user as User

Solution

  • Loadash's groupBy returns an object where each property is an array of the items that share the group key.

    So:

    const things = [{ name: 'foo', tag: 'a' }, { name: 'bar', tag: 'a' }]
    const grouped = _.groupBy(thing => thing.tag)
    // { 'a': [{ name: 'foo', tag: 'a' }, { name: 'bar', tag: 'a' }] }
    

    Which means you need your type to be { [_id: string]: User[] } (note the [] to make that an array).

    With that one addition, your code works just fine:

    const users: { [_id: string]: User[] } = _.groupBy(await db.Users.find(), '_id');
    

    See Playground


    But, considering this is a list of users from a database, they probably all have a unique _id. Which means you probably don't want the groupBy method at all, because you do not want groups of users. You probably want keyBy instead, which returns an object where some property each array member becomes a key in the returned object.

    So if you change it to keyBy then your original type works just fine:

    const users: { [_id: string]: User } = _.keyBy(await db.Users.find(), '_id');
    

    See Playground


    Lastly, you probably don't even need an explicit type here. You let Typescript infer the type of users from usage, and it will also just work fine.

    const users = _.keyBy(await db.Users.find(), '_id');
    const user = users['x'] // type: User
    

    See Playground