diff --git a/source/code-snippets/typescript/dot-notation.ts b/source/code-snippets/typescript/dot-notation.ts index 631e5508a..b526a9c16 100644 --- a/source/code-snippets/typescript/dot-notation.ts +++ b/source/code-snippets/typescript/dot-notation.ts @@ -15,11 +15,11 @@ const collection = database.collection(""); await collection.updateOne({}, { $set: { field: { nested: "A string" } } }); // end-error // start-no-key -interface TestNumber { - myNumber: number; +interface User { + email: string; } -const database = client.db(""); -const collection = db.collection("..."); -collection.find({ someRandomKey: "Accepts any type!" }); +const database = client.db(""); +const collection = db.collection(""); +collection.find({ age: "Accepts any type!" }); // end-no-key diff --git a/source/code-snippets/typescript/extend-document.ts b/source/code-snippets/typescript/extend-document.ts index 26e500ea5..127755637 100644 --- a/source/code-snippets/typescript/extend-document.ts +++ b/source/code-snippets/typescript/extend-document.ts @@ -1,7 +1,6 @@ interface Pet { name: string; age: number; - cute: true; } const database = client.db(""); diff --git a/source/code-snippets/typescript/note-on-dot-notation.ts b/source/code-snippets/typescript/note-on-dot-notation.ts deleted file mode 100644 index 7829b79b5..000000000 --- a/source/code-snippets/typescript/note-on-dot-notation.ts +++ /dev/null @@ -1,18 +0,0 @@ -// start-no-doc -// returns no documents -collection.find({ field: { s1: "hi" } }); -// end-no-doc -// start-doc -// returns your document (uses dot notation) -collection.find({ "field.s1": "hi" }); - -// returns your document (does not use dot notation) -collection.find({ - $jsonSchema: { - required: ["field"], - properties: { - field: { bsonType: "object", properties: { s1: { enum: ["hi"] } } }, - }, - }, -}); -// end-doc diff --git a/source/fundamentals/typescript.txt b/source/fundamentals/typescript.txt index c85aad9e0..004d1fff4 100644 --- a/source/fundamentals/typescript.txt +++ b/source/fundamentals/typescript.txt @@ -36,22 +36,26 @@ All classes that accept a type parameter in the driver have the default type [key: string]: any; } -Any object type can extend the ``Document`` interface. +All object types extend the ``Document`` interface. For more information on object types, see the `TypeScript handbook `__. -Extend Document -~~~~~~~~~~~~~~~ +Type Parameters that Extend Document +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following classes accept any type that extends the ``Document`` -interface: +The following classes accept all types that both extend +the ``Document`` interface and are not mutually recursive: + +.. _node-mongodb-type-parameters-extend-document: - `Collection <{+api+}/classes/Collection.html>`__ - `ChangeStream <{+api+}/classes/ChangeStream.html>`__ You can pass a type parameter that extends the ``Document`` interface like this: +.. _mongodb-node-typescript-pet-interface: + .. literalinclude:: /code-snippets/typescript/extend-document.ts :language: typescript :linenos: @@ -67,10 +71,16 @@ You can pass a type parameter that extends the ``Document`` interface like this: :start-after: start-no-key :end-before: end-no-key -Any Type -~~~~~~~~ +To view an example of a mutually recursive type, which is not supported by the +:ref:`preceding classes `, +see the :ref:`` section. + +Type Parameters of Any Type +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following classes accept any type parameter: +The following classes accept all type parameters that are not mutually recursive: + +.. _node-mongodb-type-parameters-any-type: - `FindCursor <{+api+}/classes/FindCursor.html>`__ - `AggregationCursor <{+api+}/classes/AggregationCursor.html>`__ @@ -79,81 +89,60 @@ You can find a code snippet that shows how to specify a type for the ``FindCurso class in the :ref:`Find Multiple Documents Usage Example `. -Limitations ------------ - -.. _node-driver-typescript-limitations-dot-notation: - -The driver cannot infer the type of values with keys containing **dot -notation**. Dot notation is a property access syntax for navigating BSON objects. -Click on the tabs to see code snippets that highlight this behavior: - -.. tabs:: - - .. tab:: Dot Notation - :tabid: dot-notation - - The following code snippet does not raise a type error: +To view an example of a mutually recursive type, which is not supported by the +:ref:`preceding classes `, +see the :ref:`` section. - .. literalinclude:: /code-snippets/typescript/dot-notation.ts - :language: typescript - :linenos: - :start-after: start-no-error - :end-before: end-no-error - .. tab:: Nested Objects - :tabid: nested-objects +Type Safety and Dot Notation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The following code snippet raises a type error: +If you specify a query or update with **dot notation**, the {+driver-short+} +provides type safety if your query or update does not +:ref:`reference a nested instance of a recursive type `. +Dot notation is a syntax you can use to navigate nested JSON objects. - .. literalinclude:: /code-snippets/typescript/dot-notation.ts - :language: typescript - :linenos: - :start-after: start-error - :end-before: end-error +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: - This is the error: +.. code-block:: typescript - .. code-block:: text + interface ClassificationPet { + name: string; + age: number; + classification: { genus: "Canis" | "Felis"; color: string }; + } - Type 'string' is not assignable to type 'number'. +The following code snippet correctly raises a type error when specifying +the genus of an unsupported animal in a query: -Despite the lack of type safety, we still recommend that you use dot notation to -access nested fields in query and update documents when you use TypeScript. You -must manually check that your nested field values have your intended type. +.. code-block:: typescript -.. note:: Reason To Use Dot Notation + database + .collection("") + .find({ "classification.genus": "Sylvilagus" }); - In the MongoDB Query Language, you must match a subdocument exactly - when specifying subdocuments in a query. Dot notation allows you to query - nested fields without matching subdocuments exactly. +The type error raised by the preceding code snippet is as follows: - To show this behavior, lets say you have a collection containing - only the following document: +.. code-block:: none - .. code-block:: json + No overload matches this call. + ... + Type '"Sylvilagus"' is not assignable to type 'Condition<"Canis" | "Felis">'. - { field: { s1: "hi", s2: "bye" } } +To learn more about dot notation, see +:manual:`Dot Notation ` +in the MongoDB manual. - The following query returns no results from this collection, as the value of - ``field`` does not exactly match ``{ s1: "hi" }``: +To learn more about the limitations of dot notation in the +{+driver-short+}, see the +:ref:`` +section. - .. literalinclude:: /code-snippets/typescript/note-on-dot-notation.ts - :language: typescript - :linenos: - :start-after: start-no-doc - :end-before: end-no-doc - - The following queries both return your document: - - .. literalinclude:: /code-snippets/typescript/note-on-dot-notation.ts - :language: typescript - :linenos: - :start-after: start-doc - :end-before: end-doc +.. _node-driver-limitations: - The syntax of the query that does not use dot notation is cumbersome and hard - to understand, and may not be worth the type safety obtained from - avoiding dot notation. +Limitations of Driver Version {+version+} +--------------------------------- -For more information on dot notation, see :manual:`the MongoDB Manual `. +.. include:: includes/limitations/{+version+}.rst diff --git a/source/includes/limitations/4.3.rst b/source/includes/limitations/4.3.rst new file mode 100644 index 000000000..e73db1a3a --- /dev/null +++ b/source/includes/limitations/4.3.rst @@ -0,0 +1,111 @@ +Learn about the following TypeScript specific limitations of +version 4.3 of the {+driver-short+}: + +- :ref:`No type safety for dot notation references to nested instances of recursive types ` +- :ref:`No mutually recursive types ` + +.. _node-driver-recursive-types-dot-notation: + +Recursive Types and Dot Notation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The {+driver-short+} 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 :ref:`Pet ` interface +to be recursive by allowing a pet to have its own pet. The following is the +recursive ``Pet`` interface: + +.. _node-driver-limitations-recursive-pet: + +.. code-block:: typescript + :emphasize-lines: 2 + + interface RecursivePet { + pet?: RecursivePet; + name: string; + age: number; + } + +.. note:: Depth Limit + + The {+driver-short+} 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 +:ref:`RecursivePet ` interface +with an incorrect type using dot notation, but the TypeScript compiler +does not raise a type error: + +.. code-block:: typescript + :emphasize-lines: 3 + + database + .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: + +.. code-block:: typescript + :emphasize-lines: 3 + + database + .collection("") + .findOne({ pet: "Spot" }); + +The error raised by the preceding code snippet is as follows: + +.. code-block:: none + + 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'. + +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 +:manual:`Dot Notation ` +in the MongoDB manual. + +.. _node-driver-limitations-mutual-recursion: + +Mutual Recursion +~~~~~~~~~~~~~~~~ + +You cannot specify a **mutually recursive** type as a type parameter in version +4.3 of the driver. + +A mutually recursive type exists when two types contain a property that is of +the other's type. You can update the +:ref:`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: + +.. code-block:: typescript + :emphasize-lines: 2, 8 + + 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: + +.. code-block:: none + + error TS2615: Type of property 'r' circularly references itself in mapped type '{ [Key in keyof MutuallyRecursive]... + +If you must apply a mutually recursive type to your classes, use version 4.2 of +the {+driver-short+}. diff --git a/source/usage-examples/bulkWrite.txt b/source/usage-examples/bulkWrite.txt index 5d7fe2a82..cd1112c5d 100644 --- a/source/usage-examples/bulkWrite.txt +++ b/source/usage-examples/bulkWrite.txt @@ -87,12 +87,6 @@ to ``bulkWrite()`` includes examples of ``insertOne``, ``updateMany``, and :language: typescript :linenos: - .. important:: Dot Notation Loses Type Safety - - You lose type-safety for values when you use dot notation in keys. For - more information, see our guide on - :ref:`TypeScript in the driver `. - When you run the preceding example, you should see the following output: .. code-block:: javascript