Typed Collections

Typed Collections with TypeScript

The driver ships its own type declarations, so nothing extra is installed, and collection<T>() is where the types enter. Filters, updates, projections and results are then checked against T: insertOne takes OptionalId<T> (_id optional, the rest required), and a result is WithId<T> — T with _id mandatory, because the server always sends one back.

books.ts — a typed collection, compiled with tsc 7.0.2 in strict modeTypeScript
import { Collection, MongoClient, ObjectId, WithId, OptionalId } from "mongodb";
export interface Book {
  _id?: ObjectId;
  isbn: string; title: string; year: number; tags: string[];
}
const client = new MongoClient("mongodb://127.0.0.1:28111/?directConnection=true");
const books: Collection<Book> = client.db("bookshelf").collection<Book>("catalog");
export const add = (doc: OptionalId<Book>) => books.insertOne(doc);
export const newest = (): Promise<WithId<Book> | null> =>
  books.findOne({}, { sort: { year: -1 } });
await books.find({ year: { $gt: "x" } }).toArray();     // these two lines are errors
(await newest())!.nope;
await books.findOne({ titel: "Dune" });                 // but this one compiles
Output
books.ts(13,28): error TS2769: No overload matches this call.
  The last overload gave the following error.
    Type 'string' is not assignable to type 'number'.
books.ts(14,19): error TS2339: Property 'nope' does not exist on type 'WithId<Book>'.

The checking goes deeper than field names: $gt: "x" fails because a comparison operator on year must hold a number, and $inc on a string field is rejected the same way. Know the limit, though — line 15 compiles. Filter<T> has to accept dot-notation keys such as "publisher.city", so a misspelled top-level field in a filter is legal TypeScript and matches nothing at runtime. The types catch shape errors in what you write, not typos in what you ask for. And nothing stops a document written last year from violating this year's interface, so validate at the boundary (Indexes and Query Performance) — the gap an ODM sets out to manage.