diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index bad06329c4bfa..f6a77c74f7a22 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -809,6 +809,7 @@ ExtensionArray
 - Bug in :class:`Categorical` when constructing with an :class:`Index` with :class:`ArrowDtype` (:issue:`60563`)
 - Bug in :meth:`.arrays.ArrowExtensionArray.__setitem__` which caused wrong behavior when using an integer array with repeated values as a key (:issue:`58530`)
 - Bug in :meth:`ArrowExtensionArray.factorize` where NA values were dropped when input was dictionary-encoded even when dropna was set to False(:issue:`60567`)
+- Bug in :meth:`ExtensionArray.to_numpy` raising ``TypeError`` on a tz-aware series with ``NaT`` (:issue:`59772`)
 - Bug in :meth:`api.types.is_datetime64_any_dtype` where a custom :class:`ExtensionDtype` would return ``False`` for array-likes (:issue:`57055`)
 - Bug in comparison between object with :class:`ArrowDtype` and incompatible-dtyped (e.g. string vs bool) incorrectly raising instead of returning all-``False`` (for ``==``) or all-``True`` (for ``!=``) (:issue:`59505`)
 - Bug in constructing pandas data structures when passing into ``dtype`` a string of the type followed by ``[pyarrow]`` while PyArrow is not installed would raise ``NameError`` rather than ``ImportError`` (:issue:`57928`)
diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py
index dad38abccf4ee..85b8bf86d19a0 100644
--- a/pandas/core/arrays/base.py
+++ b/pandas/core/arrays/base.py
@@ -592,7 +592,17 @@ def to_numpy(
         -------
         numpy.ndarray
         """
-        result = np.asarray(self, dtype=dtype)
+        if dtype == "datetime64" and self.dtype.kind == "M":
+            # Make sure NaT is not tz_aware
+            result = np.array(
+                [
+                    np.datetime64("NaT", "s") if isna(x) else x.tz_localize(None).asm8
+                    for x in self
+                ],
+                dtype="datetime64[s]",
+            )
+        else:
+            result = np.asarray(self, dtype=dtype)
         if copy or na_value is not lib.no_default:
             result = result.copy()
         if na_value is not lib.no_default:
diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py
index d1ef29b0bf8a0..519e4ba2f39a6 100644
--- a/pandas/tests/arrays/test_datetimelike.py
+++ b/pandas/tests/arrays/test_datetimelike.py
@@ -1356,3 +1356,10 @@ def test_from_pandas_array(dtype):
     result = idx_cls(arr)
     expected = idx_cls(data)
     tm.assert_index_equal(result, expected)
+
+
+def test_to_numpy_with_NaT_tz_aware():
+    # GH#59772
+    result = pd.Series(NaT).dt.tz_localize("UTC").to_numpy("datetime64")
+    expected = pd.Series(NaT).to_numpy("datetime64")
+    tm.assert_equal(result, expected)