Skip to content

(DOCSP-20071) Update TS Limitations #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
70649c4
remove limitation and add admonition
biniona-mongodb Jan 11, 2022
fd76f80
wip
biniona-mongodb Jan 11, 2022
438156c
wip
biniona-mongodb Jan 11, 2022
42cae8c
wip
biniona-mongodb Jan 11, 2022
fc3537b
wip
biniona-mongodb Jan 11, 2022
1853bf5
grammar check, move to own section
biniona-mongodb Jan 11, 2022
ea95abc
proofread
biniona-mongodb Jan 11, 2022
b35c0d9
CC - edits part 1
biniona-mongodb Jan 12, 2022
a758cdd
cc - break recursive example intro into sentence
biniona-mongodb Jan 12, 2022
4ba4c20
typo
biniona-mongodb Jan 12, 2022
47ec306
consistency
biniona-mongodb Jan 12, 2022
079cd5e
cc - edits
biniona-mongodb Jan 12, 2022
1be07ca
update subheadings
biniona-mongodb Jan 12, 2022
f61c9f1
test
biniona-mongodb Jan 12, 2022
7e36683
Revert "test"
biniona-mongodb Jan 12, 2022
f2ec69e
any -> all
biniona-mongodb Jan 12, 2022
eb77c53
cc - discussion
biniona-mongodb Jan 12, 2022
2d3cc37
edits
biniona-mongodb Jan 12, 2022
b48748e
warning
biniona-mongodb Jan 12, 2022
e1648d0
cc - edits
biniona-mongodb Jan 13, 2022
ad6c58d
Merge branch 'master' into DOCSP-20071-Support-Dot-Notation-TS
biniona-mongodb Jan 13, 2022
f45b0a8
update limitations for Node PR #3102
biniona-mongodb Jan 18, 2022
fe64c9f
typo
biniona-mongodb Jan 18, 2022
353416a
proofread
biniona-mongodb Jan 18, 2022
4bfa35e
wip
biniona-mongodb Jan 18, 2022
f7967df
grammar-check
biniona-mongodb Jan 18, 2022
e3f65ec
updates from slack thread
biniona-mongodb Jan 19, 2022
8bd69e4
proofread
biniona-mongodb Jan 19, 2022
06de329
proofread
biniona-mongodb Jan 19, 2022
8fb218b
cc - edits
biniona-mongodb Jan 20, 2022
6276c5d
proofread, spell check, grammar check
biniona-mongodb Jan 20, 2022
36730a5
cc - edits
biniona-mongodb Jan 24, 2022
67dc810
move header out of include to avoid h3
biniona-mongodb Jan 24, 2022
cc4fdb1
tweak
biniona-mongodb Jan 24, 2022
ad3b47e
dp - edits
biniona-mongodb Jan 31, 2022
59ed5e0
tweaks
biniona-mongodb Jan 31, 2022
7a1748f
proofread
biniona-mongodb Jan 31, 2022
8c97f31
tweak
biniona-mongodb Jan 31, 2022
11ba220
tweak example as genus and family are not independent
biniona-mongodb Jan 31, 2022
5a41582
dp - edit
biniona-mongodb Jan 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions source/code-snippets/typescript/dot-notation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ const collection = database.collection<TestType>("<your collection>");
await collection.updateOne({}, { $set: { field: { nested: "A string" } } });
// end-error
// start-no-key
interface TestNumber {
myNumber: number;
interface Furniture {
style: string;
}

const database = client.db("<your db>");
const collection = db.collection<TestNumber>("...");
collection.find({ someRandomKey: "Accepts any type!" });
const database = client.db("<your database>");
const collection = db.collection<Furniture>("<your collection>");
collection.find({ quantity: "Accepts any type!" });
// end-no-key
1 change: 0 additions & 1 deletion source/code-snippets/typescript/extend-document.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
interface Pet {
name: string;
age: number;
cute: true;
}

const database = client.db("<your database>");
Expand Down
18 changes: 0 additions & 18 deletions source/code-snippets/typescript/note-on-dot-notation.ts

This file was deleted.

165 changes: 104 additions & 61 deletions source/fundamentals/typescript.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,19 @@ Any object type can extend the ``Document`` interface.
For more information on object types, see the
`TypeScript handbook <https://www.typescriptlang.org/docs/handbook/2/objects.html>`__.

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:

- `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:
Expand All @@ -67,10 +69,13 @@ 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
preceding classes, see the :ref:`<node-driver-limitations-mutual-recursion>` 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:

- `FindCursor <{+api+}/classes/FindCursor.html>`__
- `AggregationCursor <{+api+}/classes/AggregationCursor.html>`__
Expand All @@ -79,81 +84,119 @@ 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 <node-driver-find-usage-example-code-snippet>`.

Limitations
-----------
To view an example of a mutually recursive type, which is not supported by the
preceding classes, see the :ref:`<node-driver-limitations-mutual-recursion>` section.

.. _node-driver-typescript-limitations-dot-notation:
.. _node-driver-limitations:

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:
Limitations For Driver Version {+version+}
----------------------------------

.. tabs::
The following subsections describe the TypeScript specific limitations of
version {+version+} of the {+driver-long+}.

.. tab:: Dot Notation
:tabid: dot-notation
Many limitations of the {+driver-short+} relate to **recursive types**.
A recursive type is a type that references itself. You can update
the :ref:`Pet <mongodb-node-typescript-pet-interface>` interface
to be recursive by allowing a pet to have its own pet. The following is the
recursive ``Pet`` interface:

The following code snippet does not raise a type error:
.. _node-driver-limitations-recursive-pet:

.. literalinclude:: /code-snippets/typescript/dot-notation.ts
:language: typescript
:linenos:
:start-after: start-no-error
:end-before: end-no-error
.. code-block:: typescript
:emphasize-lines: 2

.. tab:: Nested Objects
:tabid: nested-objects
interface RecursivePet {
pet?: RecursivePet;
name: string;
age: number;
}

The following code snippet raises a type error:
Recursive Types and Dot Notation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. literalinclude:: /code-snippets/typescript/dot-notation.ts
:language: typescript
:linenos:
:start-after: start-error
:end-before: end-error
The {+driver-short+} cannot provide type safety within nested instances of
recursive types referenced through **dot notation**. Dot notation is a property
access syntax for navigating BSON objects.

This is the error:
For example, the following code snippet references a nested instance of the
:ref:`RecursivePet <node-driver-limitations-recursive-pet>` interface
with an incorrect type using dot notation, but the TypeScript compiler
does not raise an error:

.. code-block:: text
.. code-block:: typescript
:emphasize-lines: 3

Type 'string' is not assignable to type 'number'.
database
.collection<RecursivePet>("your-collection")
.findOne({ "pet.age": "Spot" });

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.
The following code snippet references a top-level instance of the
``RecursivePet`` interface with an incorrect type and raises a type error:

.. note:: Reason To Use Dot Notation
.. tabs::

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.
.. tab:: Code Snippet
:tabid: code-snippet

To show this behavior, lets say you have a collection containing
only the following document:
.. code-block:: typescript
:emphasize-lines: 3

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

{ field: { s1: "hi", s2: "bye" } }
.. tab:: Error
:tabid: error

The following query returns no results from this collection, as the value of
``field`` does not exactly match ``{ s1: "hi" }``:
.. code-block:: text

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>'.

.. literalinclude:: /code-snippets/typescript/note-on-dot-notation.ts
:language: typescript
:linenos:
:start-after: start-no-doc
:end-before: end-no-doc
If you must have type safety within nested instances of recursive types,
you must write your query or update without dot notation.

The following queries both return your document:
To learn more about dot notation, see
:manual:`Dot Notation </core/document/#dot-notation>`
in the MongoDB manual.

.. literalinclude:: /code-snippets/typescript/note-on-dot-notation.ts
:language: typescript
:linenos:
:start-after: start-doc
:end-before: end-doc
.. _node-driver-limitations-mutual-recursion:

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.
Mutual Recursion
~~~~~~~~~~~~~~~~

You cannot specify a mutually recursive type as a type parameter in version
{+version+} of the driver.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this apply to all versions of the driver? If not, here's how I might structure this to prevent errors:

  • Separate all text related to a specific version into a separate file
  • Name the separate file "-4.3.txt" and include it
  • It is then easier for anyone to identify that the text only belongs to v4.3.

This may become increasingly valuable when we need to apply changes to multiple driver versions of this page in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applies to all instances.


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

.. code-block:: text

error TS2615: Type of property 'r' circularly references itself in mapped type '{ [Key in keyof MutuallyRecursive]: MutuallyRecursive[Key] extends MutuallyRecursive ? [Key] : MutuallyRecursive extends MutuallyRecursive[Key] ? [...] : MutuallyRecursive[Key] extends readonly (infer ArrayType)[] ? MutuallyRecursive extends ArrayType ? [...] : ArrayType extends MutuallyRecursive ? [...] : [...] : [...'.

A mutually recursive type exists when two types define themselves relative
to each other. You can update the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed a comment because of my own misunderstanding.
I wonder if it could be helpful to clarify "define themselves relative to each other" as that is ambiguous. Maybe "mutual recursive types exist when two interfaces contain a property type of the other".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what "property type" is referring to in the preceding comment. I think the following sentence is the intended suggestion:

"A mutually recursive type exists when two types contain a property that is of the other's type".

I'll update "relative to each other" to the preceding sentence, as I agree it is more precise.

Copy link
Contributor

@ccho-mongodb ccho-mongodb Jan 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for clarification, I used "property type", which seems consistent with TypeScript nomenclature according to their documentation. An example of a property type of a Type in your MutuallyRecursivePet interface is "string" for (name) or "number" (for age). Let me know if my terminology is incorrect and what the appropriate term is if so.

"Type" is unfortunately and maybe unavoidably ambiguous in the sentence, so I omitted it explicitly in my suggestion.

:ref:`Pet <mongodb-node-typescript-pet-interface>` interface
to be mutually recursive by allowing a pet to have a handler, and defining a
handler to have a pet. The following is the mutually
recursive ``Pet`` interface:

.. code-block:: typescript
:emphasize-lines: 2, 8

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

interface Handler {
pet: MutuallyRecursivePet;
name: string;
}

For more information on dot notation, see :manual:`the MongoDB Manual </core/document/#dot-notation>`.
If you must apply a mutually recursive type to your classes, use version 4.2 of
the {+driver-long+}.
6 changes: 0 additions & 6 deletions source/usage-examples/bulkWrite.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 <node-driver-typescript-limitations-dot-notation>`.

When you run the preceding example, you should see the following output:

.. code-block:: javascript
Expand Down