diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index a60419f89a3..fef1b988f01 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -87,6 +87,8 @@ Bug fixes
 - Fix a regression in :py:meth:`Dataset.drop`: allow passing any
   iterable when dropping variables (:issue:`3552`, :pull:`3693`)
   By `Justus Magin <https://github.com/keewis>`_.
+- Fixed errors emitted by ``mypy --strict`` in modules that import xarray.
+  (:issue:`3695`) by `Guido Imperiale <https://github.com/crusaderky>`_.
 
 Documentation
 ~~~~~~~~~~~~~
diff --git a/xarray/__init__.py b/xarray/__init__.py
index 394dd0f80bc..0b44c293b3f 100644
--- a/xarray/__init__.py
+++ b/xarray/__init__.py
@@ -1,5 +1,4 @@
 """ isort:skip_file """
-# flake8: noqa
 
 from ._version import get_versions
 
@@ -42,3 +41,55 @@
 from . import testing
 
 from .core.common import ALL_DIMS
+
+# A hardcoded __all__ variable is necessary to appease
+# `mypy --strict` running in projects that import xarray.
+__all__ = (
+    # Sub-packages
+    "ufuncs",
+    "testing",
+    "tutorial",
+    # Top-level functions
+    "align",
+    "apply_ufunc",
+    "as_variable",
+    "auto_combine",
+    "broadcast",
+    "cftime_range",
+    "combine_by_coords",
+    "combine_nested",
+    "concat",
+    "decode_cf",
+    "dot",
+    "full_like",
+    "load_dataarray",
+    "load_dataset",
+    "map_blocks",
+    "merge",
+    "ones_like",
+    "open_dataarray",
+    "open_dataset",
+    "open_mfdataset",
+    "open_rasterio",
+    "open_zarr",
+    "register_dataarray_accessor",
+    "register_dataset_accessor",
+    "save_mfdataset",
+    "set_options",
+    "show_versions",
+    "where",
+    "zeros_like",
+    # Classes
+    "CFTimeIndex",
+    "Coordinate",
+    "DataArray",
+    "Dataset",
+    "IndexVariable",
+    "Variable",
+    # Exceptions
+    "MergeError",
+    "SerializationWarning",
+    # Constants
+    "__version__",
+    "ALL_DIMS",
+)
diff --git a/xarray/testing.py b/xarray/testing.py
index 5c3ca8a3cca..ac189f7e023 100644
--- a/xarray/testing.py
+++ b/xarray/testing.py
@@ -10,6 +10,13 @@
 from xarray.core.indexes import default_indexes
 from xarray.core.variable import IndexVariable, Variable
 
+__all__ = (
+    "assert_allclose",
+    "assert_chunks_equal",
+    "assert_equal",
+    "assert_identical",
+)
+
 
 def _decode_string_data(data):
     if data.dtype.kind == "S":
diff --git a/xarray/ufuncs.py b/xarray/ufuncs.py
index ae2c5c574b6..8ab2b7cfe31 100644
--- a/xarray/ufuncs.py
+++ b/xarray/ufuncs.py
@@ -132,14 +132,68 @@ def _create_op(name):
     return func
 
 
-__all__ = """logaddexp logaddexp2 conj exp log log2 log10 log1p expm1 sqrt
-             square sin cos tan arcsin arccos arctan arctan2 hypot sinh cosh
-             tanh arcsinh arccosh arctanh deg2rad rad2deg logical_and
-             logical_or logical_xor logical_not maximum minimum fmax fmin
-             isreal iscomplex isfinite isinf isnan signbit copysign nextafter
-             ldexp fmod floor ceil trunc degrees radians rint fix angle real
-             imag fabs sign frexp fmod
-             """.split()
+__all__ = (  # noqa: F822
+    "angle",
+    "arccos",
+    "arccosh",
+    "arcsin",
+    "arcsinh",
+    "arctan",
+    "arctan2",
+    "arctanh",
+    "ceil",
+    "conj",
+    "copysign",
+    "cos",
+    "cosh",
+    "deg2rad",
+    "degrees",
+    "exp",
+    "expm1",
+    "fabs",
+    "fix",
+    "floor",
+    "fmax",
+    "fmin",
+    "fmod",
+    "fmod",
+    "frexp",
+    "hypot",
+    "imag",
+    "iscomplex",
+    "isfinite",
+    "isinf",
+    "isnan",
+    "isreal",
+    "ldexp",
+    "log",
+    "log10",
+    "log1p",
+    "log2",
+    "logaddexp",
+    "logaddexp2",
+    "logical_and",
+    "logical_not",
+    "logical_or",
+    "logical_xor",
+    "maximum",
+    "minimum",
+    "nextafter",
+    "rad2deg",
+    "radians",
+    "real",
+    "rint",
+    "sign",
+    "signbit",
+    "sin",
+    "sinh",
+    "sqrt",
+    "square",
+    "tan",
+    "tanh",
+    "trunc",
+)
+
 
 for name in __all__:
     globals()[name] = _create_op(name)