-
Type: Bug
-
Resolution: Fixed
-
Priority: Critical - P2
-
Affects Version/s: None
-
Component/s: None
I am trying to upgrade to Node driver version 4.0.1 (currently the @latest version) and we are blocked by the "new" types introduced in version 4.x, which do some very strange and complicated things with generic types. We have types for over 35 collections and the switch to mongodb@4.x (notwithstanding actual API changes, which I have duly fixed) mean our app code no longer compiles with TypeScript.
There are two contrasting examples of what is not working, and both of them point in opposite directions:
- `const thing = db.collection<MyType>("name").findOne();` no longer returns `MyType | undefined` by default. Instead, it returns `{ _id: string } | undefined`, which is totally useless and essentially breaks Everything™.
This is due to a conflict in the type overloads for `.findOne`, specifically the ones that look like `findOne<T = TSchema>(): Promise<T | undefined>;`.
For some reason `TSchema`, which should have been set implicitly via `collection<MyType>`, reverts to its type fallback, `Document` ({ _id: string }). If I comment-out the `.findOne` overloads that contain the extra generic parameter, most of our code compiles.
BUT, this issue is also related to its complement, namely:
- The types break badly when a projection of any kind is involved. Specifically, `findOne<T = TSchema>(filter: Filter<T>, options: FindOptions<T>): Promise<T | undefined>;` breaks because `T` is inferred (via `FindOptions<T>`) to be the type of the projection object you pass in, rather than returning a restricted version of `TSchema` via `Pick` or `Exclude`. To be honest, I wouldn't care if the library did nothing fancy with `TSchema` – for now, we are smart enough to use the unmodified collection type safely. But as written that is not possible.
From my understanding, this could actually never work as written; if I pass `projection: { _id: 0, someField: 1 }` I don't ever want the result of `findOne` to be `Promise<{ _id: 0, someField: 1}>`, i.e. an object whose _id field exists and is literally a zero.
The same issue occurs with Cursors, e.g. `.find().project({ thing: 1 }).toArray()` produces totally bogus results, type-wise.
`@types/mongodb` was not perfect but it did all of this much better and for the most part "just worked". While I do welcome the driver moving to TypeScript, there appears to be way too much happening manually in the typescript files (especially via manual overloading), which is a recipe for the kind of major issues we see here.
I think most of these issues would be solved by writing the typescript in a way that allows the arguments to be inferred rather than trying to outsmart the compiler by adding 10 different overloads manually for the various functions.
I am a bit shocked that driver v4 was pushed out the door in this state: I can't imagine any TypeScript project being able to upgrade in the state the library is in. If MongoDB expects adoption of driver v4, this really needs to be fixed, fast.