Docs Menu

Docs HomeDevelop ApplicationsMongoDB DriversNode.js

TypeScript

On this page

  • Overview
  • Features
  • Type Parameters that Extend Document
  • Type Parameters of Any Type
  • Type Safety and Dot Notation
  • Insert Operations and the _id Field
  • Find Methods and the _id Field
  • Known Limitations
  • Recursive Types and Dot Notation
  • Mutual Recursion

In this guide, you can learn about the TypeScript features and limitations of the MongoDB Node.js driver. TypeScript is a strongly typed programming language that compiles to JavaScript.

The TypeScript compiler offers type checking in real time. Code editors that support TypeScript can provide autocomplete suggestions, display documentation inline, and identify type-related errors.

All TypeScript features of the driver are optional. All valid JavaScript code written with the driver is also valid TypeScript code.

For more information, see the TypeScript website.

If you use TypeScript, you can specify a type for some classes in the driver. All classes that accept a type parameter in the driver have the default type Document. The Document interface has the following definition:

interface Document {
[key: string]: any;
}

All object types extend the Document interface.

For more information on object types, see the TypeScript handbook.

The following classes accept all types that extend the Document interface:

You can pass a type parameter that extends the Document interface like this:

1interface Pet {
2 name: string;
3 age: number;
4}
5
6const database = client.db("<your database>");
7const collection = database.collection<Pet>("<your collection>");

Important

Keys Not in Type Parameter Receive any Type

Keys not listed in your specified type parameter receive the any type. The following code snippet demonstrates this behavior:

1interface User {
2 email: string;
3}
4
5const database = client.db("<your database>");
6const collection = db.collection<User>("<your collection>");
7collection.find({ age: "Accepts any type!" });

The following classes accept all type parameters:

You can find a code snippet that shows how to specify a type for the FindCursor class in the Find Multiple Documents Usage Example.

If you specify a query or update with dot notation, the Node.js driver provides type safety if your query or update does not reference a nested instance of a recursive type. Dot notation is a syntax you can use to navigate nested JSON objects.

The following code snippet defines the ClassificationPet interface, which includes a classification field that enables you to specify the genus and color of dogs and cats:

interface ClassificationPet {
name: string;
age: number;
classification: { genus: "Canis" | "Felis"; color: string };
}

The following code snippet correctly raises a type error when specifying the genus of an unsupported animal in a query:

database
.collection<ClassificationPet>("<your collection>")
.find({ "classification.genus": "Sylvilagus" });

The type error raised by the preceding code snippet is as follows:

No overload matches this call.
...
Type '"Sylvilagus"' is not assignable to type 'Condition<"Canis" | "Felis">'.

If you need to query a collection or perform another operation with a key that incorporates variables, you must use an as const assertion when specifying the key. This mechanism allows your code to compile successfully as long as the input types are correct.

The following code snippet defines the ClassificationPet interface and the Mealtime interface. ClassificationPet includes a mealtimes field that contains an array of Mealtime interfaces, each of which includes a time field:

interface ClassificationPet {
name: string;
mealtimes: Mealtime[];
}
interface Mealtime{
time: string;
amount: number;
}

The following code snippet performs a find-and-update operation on a collection of ClassificationPet documents. The operation updates the nested time field of the Mealtime instance at index 1. The index position is specified by the variable mealCounter:

const mealCounter = 1;
await collection.findOneAndUpdate(
{ name: "Lassie" },
{ $set: { [`mealtimes.${mealCounter}.time` as const]: '4:00 PM' } },
);

To learn more about dot notation, see Dot Notation in the MongoDB manual.

To learn more about the limitations of dot notation in the Node.js driver, see the Recursive Types and Dot Notation section.

How you specify the _id field in type parameters passed to your Collection instance affects the behavior of insert operations. The following table describes how different _id field specifications affect insert operations:

_id field type
Example Type
Required on insert
Behavior on insert
Unspecified
Not applicable
No
The driver creates an ObjectId value for each inserted document.
Specified
{ _id: number };
Yes
If you do not specify a value for the _id field in an insert operation, the driver raises an error.
Specified as optional
{ _id?: number };
No
If you do not specify the _id field in an insert operation, the driver adds an _id field value generated by the primary key factory.

If you must specify the _id field as required in the type you define to represent documents in your collection but you do not want to specify values for the _id field in insert operations, use the OptionalId helper type when you create your collection. The OptionalId type accepts a type parameter as an argument and returns that type with an optional _id field.

The following code snippet defines the IdPet interface, which includes a type for the _id field:

interface IdPet {
_id: ObjectId;
name: string;
age: number;
}

The following code uses the preceding interface along with the OptionalId type to insert a document without specifying a value for the _id field:

const database = client.db("<your database>");
const collection = db.collection<OptionalId<IdPet>>("<your collection>");
collection.insertOne({
name: "Spot",
age: 2
});

To learn more about the _id field, see The _id Field in the MongoDB manual.

To learn more about the types, interfaces, and classes discussed in this section, see the following resources:

The find and findOne methods of the Collection class include the _id field in their return type. The driver infers the type of the returned _id field based on the type parameter you passed to your Collection instance.

If the type parameter you passed to your Collection instance includes the _id field in its schema, the driver infers that the _id field returned from the method is of the type specified in the schema.

However, if the type parameter you passed to your Collection instance does not include the _id field in its schema, the driver infers that the type of the _id field returned from the method is ObjectId.

Tip

The type parameter passed to your Collection influences only the type inference of the fields returned from the method. The driver does not convert the field to the specified type. The type of each field in your type parameter's schema should match the type of the corresponding field in the collection.

The following code uses the Pet interface to return a document with an _id inferred to be of type ObjectId:

const database = client.db("<your database>");
const collection = db.collection<Pet>("<your collection>");
const document = await collection.findOne({
name: "Spot",
});
const id : ObjectId = document._id;

The following code uses the IdNumberPet interface to return a document with an _id inferred to be of type number:

interface IdNumberPet {
_id: number;
name: string;
age: number;
}
const database = client.db("<your database>");
const collection = db.collection<IdNumberPet>("<your collection>");
const document = await collection.findOne({
name: "Spot",
});
const id : number = document._id;

Important

Projection

If you specify a projection in a find method, you should pass a type parameter to your find method that reflects the structure of your projected documents. Without a type parameter, TypeScript cannot check at compile time that you are using your projected documents safely.

To show this behavior, the following code snippet passes type checking but raises an error at runtime:

const doc = await collection.findOne(
{},
{ projection: { _id: 0, name: 1 } }
);
console.log(doc._id.generationTime);

To catch this error at compile time, pass a type parameter that does not include the _id field to your find method:

interface ProjectedDocument {
name: string
}
const doc = await collection.findOne<ProjectedDocument>(
{},
{ projection: { _id: 0, name: 1 } }
);
// Compile time error: Property '_id' does not exist on type 'ProjectedDocument'.
console.log(doc._id.generationTime);

To view a runnable TypeScript example that includes a find method applying a projection, see the Find a Document page.

To learn more about the classes and methods discussed in this section, see the following API documentation:

Learn about the following TypeScript specific limitations of the Node.js driver:

The Node.js driver cannot provide type safety within nested instances of recursive types referenced through dot notation.

A recursive type is a type that references itself. You can update the Pet interface to be recursive by allowing a pet to have its own pet. The following is the recursive Pet interface:

interface RecursivePet {
pet?: RecursivePet;
name: string;
age: number;
}

Note

Depth Limit

The Node.js driver does not traverse nested recursive types when type checking dot notation keys to avoid hitting TypeScript's recursive depth limit.

The following code snippet references a nested instance of the RecursivePet interface with an incorrect type using dot notation, but the TypeScript compiler does not raise a type error:

database
.collection<RecursivePet>("<your collection>")
.findOne({ "pet.age": "Spot" });

The following code snippet references a top-level instance of the RecursivePet interface with an incorrect type and raises a type error:

database
.collection<RecursivePet>("<your collection>")
.findOne({ pet: "Spot" });

The error raised by the preceding code snippet is as follows:

index.ts(19,59): error TS2769: No overload matches this call.
The last overload gave the following error.
Type 'string' is not assignable to type 'Condition<Pet>'.

If you must have type safety within nested instances of recursive types, you must write your query or update without dot notation.

To learn more about dot notation, see Dot Notation in the MongoDB manual.

Important

In this driver version, you cannot specify a mutually recursive type as a type parameter. To specify a mutually recursive type as a type parameter, use version 4.11 or newer.

You cannot specify a mutually recursive type as a type parameter.

A mutually recursive type exists when two types contain a property that is of the other's type. You can update the Pet interface to be mutually recursive by allowing a pet to have a handler, and defining a handler to have a pet. The following are the mutually recursive Pet and Handler interfaces:

interface MutuallyRecursivePet {
handler?: Handler;
name: string;
age: number;
}
interface Handler {
pet: MutuallyRecursivePet;
name: string;
}

If you specify a mutually recursive type, the TypeScript compiler raises the following error:

error TS2615: Type of property 'r' circularly references itself in mapped type '{ [Key in keyof MutuallyRecursive]...
←  Time SeriesBSON Settings →