1

I have a type Mongoify that takes in a type T and removes the id key and replaces it with a _id key.

type Mongoify<T extends {id: string}> = Omit<T, "id"> & {
    _id: ObjectId
};

function fromMongo<T extends { id: string }>(x: Mongoify<T>): T {
    const { _id, ...theRest } = x;
    const  withNormalId = { ...theRest, id: _id.toHexString() };
    return withNormalId
}

For some reason, this function does not type check. I get the error:

Type 'Omit<Mongoify<T>, "_id"> & { id: string; }' is not assignable to type 'T'.
  'Omit<Mongoify<T>, "_id"> & { id: string; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ id: string; }'.(2322)

I have looked at How to fix TS2322: "could be instantiated with a different subtype of constraint 'object'"?, which explains what is going on with this error. But I'm not sure what the cause is here. My assumption is Typescript is failing to do the type inference that ...theRest is of type Omit<Mongoify<T>, "_id">, but even that seems incorrect because if I mouse over the value in the playground, Typescript shows me the correct type.

Anyone know why this function is failing to typecheck?

Foobar
  • 7,458
  • 16
  • 81
  • 161
  • in more generic form it looks like ```Type 'Pick & Omit' is not assignable to type 'T'``` – dm.shpak Jul 07 '21 at 21:24
  • What's `ObjectId`? Please consider modifying the code to be a [mcve] suitable for dropping into a standalone IDE like [The TypeScript Playground](https://tsplay.dev/NlpJlN) which demonstrates your issue. – jcalz Jul 08 '21 at 01:01

1 Answers1

0

There are literal types in typescript and thus it cannot guarantee the returned type T will match your type 'Omit<Mongoify, "_id"> & { id: string; }`. For example if you have

type Bad = { id: 'literal-string' }

You'd expect the result of:

fromMongo<Bad>({ _id: objectId })

be of type Bad but in fact your runction implementation returns type { id: string }.

To mitigate this ussue you can rewrite your function as:

function fromMongo<
    T extends { _id: ObjectId }
>(x: T): Omit<T, '_id'> & { id: string } {
    const { _id, ...theRest } = x;

    return  { ...theRest, id: _id.toHexString() };
}

playground link

aleksxor
  • 7,535
  • 1
  • 22
  • 27