There is a numpy.typing module with an NDArray generic type.


From the Numpy 2.3 docs:
numpy.typing.NDArray = numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]

A np.ndarray[tuple[Any, ...], np.dtype[ScalarT]] type alias generic w.r.t. its dtype.type.

Can be used during runtime for typing arrays with a given dtype and unspecified shape.

Examples:

>>> import numpy as np
>>> import numpy.typing as npt

>>> print(npt.NDArray)
numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]

>>> print(npt.NDArray[np.float64])
numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float64]]

>>> NDArrayInt = npt.NDArray[np.int_]
>>> a: NDArrayInt = np.arange(10)

>>> def func(a: npt.ArrayLike) -> npt.NDArray[Any]:
...     return np.array(a)

As of 2025-07-17, support for shapes is still a work in progress per numpy/numpy#16544.

Answer from Jasha on Stack Overflow
🌐
NumPy
numpy.org › doc › stable › reference › typing.html
Typing (numpy.typing) — NumPy v2.5 Manual
A comprehensive overview of all objects that can be coerced into data types. ... >>> import numpy as np >>> import numpy.typing as npt >>> def as_dtype(d: npt.DTypeLike) -> np.dtype: ... return np.dtype(d) ... A np.ndarray[tuple[Any, ...], np.dtype[ScalarT]] type alias generic w.r.t.
🌐
NumPy
numpy.org › devdocs › reference › typing.html
Typing (numpy.typing) — NumPy v2.6.dev0 Manual
A comprehensive overview of all objects that can be coerced into data types. ... >>> import numpy as np >>> import numpy.typing as npt >>> def as_dtype(d: npt.DTypeLike) -> np.dtype: ... return np.dtype(d) ... A np.ndarray[tuple[Any, ...], np.dtype[ScalarT]] type alias generic w.r.t.
Discussions

Support for `numpy.typing.NDArray`
Right now, typeguard doesn't check that the dtype is respected, and doesn't emit notifications when passed a ndarray that has the wrong dtype. I understand the eventual argument against making specific fixes. However, NumPy is one of if not the single most used third-party Python library, with ... More on github.com
🌐 github.com
20
July 8, 2021
What the latest and greatest way to type hint numpy arrays?
I am aware of two ways: numpy.ndarray[tuple[int, int], float] means rank 2 matrix of floats. Does not distinguish between a shape of (2, 3) and (7, 5) - just enforces that the shape be length 2. numpy.typing.NDArray[float] must contain floats, shape not specified. I have heard of the ability to do numpy.ndarray[tuple[typing.Literal[4]], float] to say an array of floats of shape (4,), but I have not tried it and have heard that support is lacking. More on reddit.com
🌐 r/learnpython
7
3
December 14, 2025
Type-hint a numpy NDArray with different possible dtypes
How should I type-hint a numpy ndarray which can be of any integer type? My thoughts are: from numpy.typing import DTypeLike, NDArray # option (1) arr: NDArray[+(np.int8, np.int16, np.int32, np.int64)] # option (2) ScalarIntType: tuple[DTypeLike, ...] = (np.int8, np.int16, np.int32, np.int64) ... More on discuss.python.org
🌐 discuss.python.org
1
0
February 27, 2024
python - Numpy Typing with specific shape and datatype - Stack Overflow
Copyimport numpy as np from beartype import beartype from my_types import Int32Array4 @beartype def foo(arr: Int32Array4): ... # Runtime type checked by beartype. You could also stack up both libraries. Your my_types.py can be removed again and your foo.py would become something like: Copyfrom nptyping import NDArray... More on stackoverflow.com
🌐 stackoverflow.com
Top answer
1 of 7
124

There is a numpy.typing module with an NDArray generic type.


From the Numpy 2.3 docs:
numpy.typing.NDArray = numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]

A np.ndarray[tuple[Any, ...], np.dtype[ScalarT]] type alias generic w.r.t. its dtype.type.

Can be used during runtime for typing arrays with a given dtype and unspecified shape.

Examples:

>>> import numpy as np
>>> import numpy.typing as npt

>>> print(npt.NDArray)
numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]

>>> print(npt.NDArray[np.float64])
numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float64]]

>>> NDArrayInt = npt.NDArray[np.int_]
>>> a: NDArrayInt = np.arange(10)

>>> def func(a: npt.ArrayLike) -> npt.NDArray[Any]:
...     return np.array(a)

As of 2025-07-17, support for shapes is still a work in progress per numpy/numpy#16544.

2 of 7
75

Update

Check recent numpy versions for a new typing module

https://numpy.org/doc/stable/reference/typing.html#module-numpy.typing

dated answer

It looks like typing module was developed at:

https://github.com/python/typing

The main numpy repository is at

https://github.com/numpy/numpy

Python bugs and commits can be tracked at

http://bugs.python.org/

The usual way of adding a feature is to fork the main repository, develop the feature till it is bomb proof, and then submit a pull request. Obviously at various points in the process you want feedback from other developers. If you can't do the development yourself, then you have to convince someone else that it is a worthwhile project.

cython has a form of annotations, which it uses to generate efficient C code.


You referenced the array-like paragraph in numpy documentation. Note its typing information:

A simple way to find out if the object can be converted to a numpy array using array() is simply to try it interactively and see if it works! (The Python Way).

In other words the numpy developers refuse to be pinned down. They don't, or can't, describe in words what kinds of objects can or cannot be converted to np.ndarray.

In [586]: np.array({'test':1})   # a dictionary
Out[586]: array({'test': 1}, dtype=object)

In [587]: np.array(['one','two'])  # a list
Out[587]: 
array(['one', 'two'], 
      dtype='<U3')

In [589]: np.array({'one','two'})  # a set
Out[589]: array({'one', 'two'}, dtype=object)

For your own functions, an annotation like

def foo(x: np.ndarray) -> np.ndarray:

works. Of course if your function ends up calling some numpy function that passes its argument through asanyarray (as many do), such an annotation would be incomplete, since your input could be a list, or np.matrix, etc.


When evaluating this question and answer, pay attention to the date. 484 was a relatively new PEP back then, and code to make use of it for standard Python still in development. But it looks like the links provided are still valid.

🌐
NumPy
numpy.org › doc › 2.3 › reference › typing.html
Typing (numpy.typing) — NumPy v2.3 Manual
A comprehensive overview of all objects that can be coerced into data types. ... >>> import numpy as np >>> import numpy.typing as npt >>> def as_dtype(d: npt.DTypeLike) -> np.dtype: ... return np.dtype(d) numpy.typing.NDArray = numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[~_ScalarT]]#
🌐
Medium
medium.com › data-science-collective › do-more-with-numpy-array-type-hints-annotate-validate-shape-dtype-09f81c496746
Do More with NumPy Array Type Hints: Annotate & Validate Shape & Dtype | by Christopher Ariza | Data Science Collective | Medium
May 26, 2025 - The np.ndarray generic takes two type parameters: the first defines the shape with a tuple, the second defines the element type with the generic np.dtype. While np.ndarray has taken two type parameters for some time, the definition of the first ...
🌐
Jack Atkinson
jackatkinson.net › post › numpy_typing
Typing in numpy - Jack Atkinson's Website
April 27, 2025 - Under the hood it is a union of the many types that numpy ‘agressively’ casts to arrays. Using this as the input type to functions provides a lot of flexibility as to what can be accepted - e.g. users can pass scalars, lists, and arrays. Note that there may be a small price to pay in that you might need to explicitly cast ArrayLike to an NDArray ...
🌐
GitHub
github.com › agronholm › typeguard › issues › 195
Support for `numpy.typing.NDArray` · Issue #195 · agronholm/typeguard
July 8, 2021 - NumPy has introduced a new typing library: https://numpy.org/devdocs/reference/typing.html. One part of it is that you can mention the dtype of elements in the type: import numpy.typing as npt ... def some_function( a_float_array: ...
Author: agronholm
🌐
NumPy
numpy.org › doc › 2.2 › reference › generated › numpy.ndarray.html
numpy.ndarray — NumPy v2.2 Manual
its dtype.type. ... If buffer is None, then only shape, dtype, and order are used. If buffer is an object exposing the buffer interface, then all keywords are interpreted. No __init__ method is needed because the array is fully initialized after the __new__ method. ... These examples illustrate the low-level ndarray constructor. Refer to the See Also section above for easier ways of constructing an ndarray. ... >>> import numpy as np >>> np.ndarray(shape=(2,2), dtype=float, order='F') array([[0.0e+000, 0.0e+000], # random [ nan, 2.5e-323]])
Find elsewhere
🌐
w3resource
w3resource.com › numpy › snippet › exploring-numpy-typing.php
Exploring numpy.typing for Enhanced Type Hints
3. Static Type Checking: With the corrected imports, the code will work for runtime execution and also provide static type-checking benefits when using tools like MyPy. ... import numpy as np from numpy.typing import NDArray # Define a function to perform element-wise square def square_elements(arr: NDArray[np.int32]) -> NDArray[np.int32]: return arr ** 2 # Test the function array = np.array([1, 2, 3], dtype=np.int32) squared_array = square_elements(array) print("Original array:", array) print("Squared array:", squared_array)
🌐
NumPy
numpy.org › devdocs › reference › generated › numpy.ndarray.html
numpy.ndarray — NumPy v2.6.dev0 Manual
its dtype.type. ... If buffer is None, then only shape, dtype, and order are used. If buffer is an object exposing the buffer interface, then all keywords are interpreted. No __init__ method is needed because the array is fully initialized after the __new__ method. ... Try it in your browser! These examples illustrate the low-level ndarray constructor. Refer to the See Also section above for easier ways of constructing an ndarray. ... >>> import numpy as np >>> np.ndarray(shape=(2,2), dtype=np.float64, order='F') array([[0.0e+000, 0.0e+000], # random [ nan, 2.5e-323]])
🌐
Like Geeks
likegeeks.com › home › python › python type hinting for numpy arrays
Python Type Hinting for NumPy Arrays
from numpy.typing import NDArray ... filter_positive_integers(data) print(result) ... The function filters out negative integers from the array, leaving only positive integers. You can also use common aliases like np.float64 for ...
🌐
PyPI
pypi.org › project › nptyping
nptyping · PyPI
🧊 Type hints for NumPy 🐼 Type hints for pandas.DataFrame 💡 Extensive dynamic type checks for dtypes shapes and structures 🚀 Jump to the Quickstart ... >>> from nptyping import DataFrame, Structure as S >>> df: DataFrame[S["name: Str, x: Float, y: Float"]] ... >>> import numpy as np >>> isinstance(np.array([[1, 2], [3, 4]]), NDArray[Shape["2, 2"], Int]) True >>> isinstance(np.array([[1., 2.], [3., 4.]]), NDArray[Shape["2, 2"], Int]) False >>> isinstance(np.array([1, 2, 3, 4]), NDArray[Shape["2, 2"], Int]) False
      » pip install nptyping
    
Published: Feb 20, 2023
Version: 2.5.0
🌐
NumPy
numpy.org › doc › 2.2 › reference › typing.html
Typing (numpy.typing) — NumPy v2.2 Manual
A comprehensive overview of all objects that can be coerced into data types. ... >>> import numpy as np >>> import numpy.typing as npt >>> def as_dtype(d: npt.DTypeLike) -> np.dtype: ... return np.dtype(d) numpy.typing.NDArray = numpy.ndarray[tuple[int, ...], numpy.dtype[+_ScalarType_co]][source]#
🌐
Python.org
discuss.python.org › python help
Type-hint a numpy NDArray with different possible dtypes - Python Help - Discussions on Python.org
February 27, 2024 - How should I type-hint a numpy ndarray which can be of any integer type? My thoughts are: from numpy.typing import DTypeLike, NDArray # option (1) arr: NDArray[+(np.int8, np.int16, np.int32, np.int64)] # option (2) ScalarIntType: tuple[DTypeLike, ...] = (np.int8, np.int16, np.int32, np.int64) arr: NDArray[+ScalarIntType] # option (3) arr: NDArray[np.int8] | NDArray[np.int16] | NDArray[np.int32] | NDArray[np.int64] Is one of those options valid and/or which one would be best and/or which alt...
🌐
GitHub
github.com › ramonhagenaars › nptyping
GitHub - ramonhagenaars/nptyping: 💡 Type hints for Numpy and Pandas
🧊 Type hints for NumPy 🐼 Type hints for pandas.DataFrame 💡 Extensive dynamic type checks for dtypes shapes and structures 🚀 Jump to the Quickstart · Example of a hinted numpy.ndarray: >>> from nptyping import NDArray, Int, Shape >>> arr: NDArray[Shape["2, 2"], Int] Example of a hinted pandas.DataFrame: >>> from nptyping import DataFrame, Structure as S >>> df: DataFrame[S["name: Str, x: Float, y: Float"]] Command ·
Starred by 639 users
Forked by 37 users
Languages: Python
🌐
Taoa
taoa.io › posts › Shape-typing-numpy-with-pyright-and-variadic-generics
Shape typing numpy with pyright and variadic generics - T·A·O·A
February 27, 2023 - In order to help us prevent shape errors, let's see what typing capabilities exist in numpy. As of writing this post, numpy==v1.24.2 only supports typing on an ndarray's dtype (uint8, float64, etc.).
Top answer
1 of 3
71

Currently, numpy.typing.NDArray only accepts a dtype, like so: numpy.typing.NDArray[numpy.int32]. You have some options though.

Use typing.Annotated

typing.Annotated allows you to create an alias for a type and to bundle some extra information with it.

In some my_types.py you would write all variations of shapes you want to hint:

from typing import Annotated, Literal, TypeVar
import numpy as np
import numpy.typing as npt


DType = TypeVar("DType", bound=np.generic)

Array4 = Annotated[npt.NDArray[DType], Literal[4]]
Array3x3 = Annotated[npt.NDArray[DType], Literal[3, 3]]
ArrayNxNx3 = Annotated[npt.NDArray[DType], Literal["N", "N", 3]]

And then in foo.py, you can supply a numpy dtype and use them as typehint:

import numpy as np
from my_types import Array4


def foo(arr: Array4[np.int32]):
    assert arr.shape == (4,)

MyPy will recognize arr to be an np.ndarray and will check it as such. The shape checking can be done at runtime only, like in this example with an assert.

If you don't like the assertion, you can use your creativity to define a function to do the checking for you.

def assert_match(arr, array_type):
    hinted_shape = array_type.__metadata__[0].__args__
    hinted_dtype_type = array_type.__args__[0].__args__[1]
    hinted_dtype = hinted_dtype_type.__args__[0]
    assert np.issubdtype(arr.dtype, hinted_dtype), "DType does not match"
    assert arr.shape == hinted_shape, "Shape does not match"


assert_match(some_array, Array4[np.int32])

Use nptyping

Another option would be to use 3th party lib nptyping (yes, I am the author).

You would drop my_types.py as it would be of no use anymore.

Your foo.py would become something like:

from nptyping import NDArray, Shape, Int32


def foo(arr: NDArray[Shape["4"], Int32]):
    assert isinstance(arr, NDArray[Shape["4"], Int32])

Use beartype + typing.Annotated

There is also another 3th party lib called beartype that you could use. It can take a variant of the typing.Annotated approach and will do the runtime checking for you.

You would reinstate your my_types.py with content similar to:

from beartype import beartype
from beartype.vale import Is
from typing import Annotated
import numpy as np


Int32Array4 = Annotated[np.ndarray, Is[lambda array:
    array.shape == (4,) and np.issubdtype(array.dtype, np.int32)]]
Int32Array3x3 = Annotated[np.ndarray, Is[lambda array:
    array.shape == (3,3) and np.issubdtype(array.dtype, np.int32)]]

And your foo.py would become:

import numpy as np
from beartype import beartype
from my_types import Int32Array4 


@beartype
def foo(arr: Int32Array4):
    ...  # Runtime type checked by beartype.

Use beartype + nptyping

You could also stack up both libraries.

Your my_types.py can be removed again and your foo.py would become something like:

from nptyping import NDArray, Shape, Int32
from beartype import beartype


@beartype
def foo(arr: NDArray[Shape["4"], Int32]):
    ...  # Runtime type checked by beartype.
2 of 3
25

EDIT: Shape typing has been added to numpy. Types are covariant and they must be tuple[int, ...]. This allows things like NamedTuple, tuple[Literal[1]], tuple[NewType("Frame", int)]. Your example it would be:

import numpy as np
from typing import Literal

def foo(x: np.ndarray[tuple[Literal[4]], np.dtype[np.int32]]):

Currently, shape type information is reflected in ndarray.shape. However, most numpy functions that change the dimension or size of an array, however, don't necessarily know how to handle different axes and sizes in typing. As a result something like: arr[:] will lose the shape type information from arr.

Old answer: Prior to numpy 2.1, you could put anything as the first type argument to np.ndarray[_ShapeLike, _DtypeLike_co]. There was some consideration of TypeVarTuple (see PEP 646) for array shapes. However, bounds and variance is not implemented for TypeVarTuple, and therefore it cannot currently be used as the shape parameter.

🌐
NumPy
numpy.org › doc › 1.20 › reference › typing.html
Typing (numpy.typing) — NumPy v1.20 Manual
January 31, 2021 - The timedelta64 class is not considered a subclass of signedinteger, the former only inheriting from generic while static type checking. ... A Union representing objects that can be coerced into an ndarray. ... Objects implementing the __array__ protocol. ... Any scalar or sequence that can be interpreted as an ndarray. ... >>> import numpy as np >>> import numpy.typing as npt >>> def as_array(a: npt.ArrayLike) -> np.ndarray: ...
🌐
Python.org
discuss.python.org › python help
How to type annotate mathematical operations that supports built-in numerics, collections and numpy arrays? - Python Help - Discussions on Python.org
April 2, 2022 - In scientific code that deals with mathematical operations, before type annotations, it is common to assume that any mathematical function could take in a numpy array. To be explicit you just add to your docstring that you can take array_like or ndarray (as is conventional in NumFocus project docs).