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.
Answer from R H on Stack Overflow
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 › devdocs › reference › typing.html
Typing (numpy.typing) — NumPy v2.6.dev0 Manual
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.
Discussions

Typing support for shapes
See how contracts package are trying to provide support for something similar to shapes. They are extending annotations in a different way than just standard typing and maybe something like that co... More on github.com
🌐 github.com
55
December 6, 2017
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
Numpydantic - array typing and validation for pydantic and beyond
Hello everyone! @stefanv recommended i share this here, hopefully y’all find it interesting! I just released a package numpydantic that provides generic typing for array shape and dtype validation using pydantic (and also generally), post here: Dr. jonny phd: "Here's an ~ official ~ release ... More on discuss.scientific-python.org
🌐 discuss.scientific-python.org
1
2
May 25, 2024
ENH: numpy.typing for type checking, documentation and Numpy compilers
Proposed new feature or change: Type annotations in code using Numpy (and other Python array libraries) can be used for 3 purposes: Python-Numpy compilers (for example Cython or Pythran) documentat... More on github.com
🌐 github.com
2
May 3, 2024
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.

🌐
Jim Fisher
jameshfisher.com › 2024 › 04 › 12 › shape-typing-in-python
Shape typing in Python - Jim Fisher
April 12, 2024 - This uses Numpy’s np.ndarray type, which takes two arguments that describe the shape and dtype. For example, we can describe a 2x3 matrix of integers as: mat2x3: np.ndarray[ tuple[Literal[2], Literal[3]], np.dtype[np.int64], ] = ...
🌐
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 · 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"]] Example of instance checking: >>> 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
🌐
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 - Shape typing is a technique used to annotate information about the dimensionality and size of an array. In the context of numpy and the python type hinting system, we can use shape typing catch shape errors before runtime.
🌐
W3Schools
w3schools.com › python › numpy › numpy_array_shape.asp
NumPy Array Shape
import numpy as np arr = np.array([1, 2, 3, 4], ndmin=5) print(arr) print('shape of array :', arr.shape) Try it Yourself »
🌐
GitHub
github.com › numpy › numpy › issues › 16544
Typing support for shapes · Issue #16544 · numpy/numpy
December 6, 2017 - They are extending annotations in a different way than just standard typing and maybe something like that could be also done. So instead of providing specific extension (PEP) for typing to allow thing like shapes, it might be maybe more useful to determine a syntax for general constraints on types and use that, in addition to standard types through typing.
Author: numpy
Find elsewhere
🌐
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 NumPy array object can take many concrete forms. It might be a one-dimensional (1D) array of Booleans, or a three-dimensional (3D) array of 8-bit unsigned integers. As the built-in function isinstance() will show, every array is an instance of np.ndarray, regardless of shape or the type of elements stored in the array, i.e., the dtype.
🌐
Scientific Python
discuss.scientific-python.org › contributor & development discussion
Numpydantic - array typing and validation for pydantic and beyond - Contributor & Development Discussion - Scientific Python
May 25, 2024 - Hello everyone! @stefanv recommended i share this here, hopefully y’all find it interesting! I just released a package numpydantic that provides generic typing for array shape and dtype validation using pydantic (and also generally), post here: Dr. jonny phd: "Here's an ~ official ~ release announcement for #…" - Neuromatch Social The idea is that we not only want to be able to specify arrays in pydantic, specify shape and dtype constraints for those arrays, but also be able to ...
🌐
w3resource
w3resource.com › numpy › snippet › exploring-numpy-typing.php
Exploring numpy.typing for Enhanced Type Hints
Comprehensive Guide to numpy.typing in Python · numpy.typing is a submodule introduced in NumPy 1.21.0 that provides type hints for static type checkers like MyPy. It enhances code clarity, reduces errors, and improves editor support by specifying ...
🌐
Like Geeks
likegeeks.com › home › python › python type hinting for numpy arrays
Python Type Hinting for NumPy Arrays
The function normalizes the array so that it has a mean of 0 and a standard deviation of 1. You can combine shape and dtype for more precise type hints.
🌐
GitHub
github.com › numpy › numpy › issues › 26380
ENH: numpy.typing for type checking, documentation and Numpy compilers · Issue #26380 · numpy/numpy
May 3, 2024 - I'm not saying that numpy.typing should support such things but it seems to me that it is important when designing numpy.typing to consider the different purposes of type annotations in code using Numpy and not to be mostly focus on what is currently supported by Mypy. Simple things like specifying that an array is a one or two-dimensional array of float64 should be simple and short with numpy.typing.
Author: numpy
🌐
NumPy
numpy.org › doc › stable › reference › typing.html
Typing (numpy.typing) — NumPy v2.5 Manual
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.
🌐
Cython
cython.readthedocs.io › en › latest › src › tutorial › numpy.html
Working with NumPy — Cython 3.3.0 documentation
All other use (attribute lookup or indexing) can potentially segfault or corrupt data (rather than raising exceptions as they would in Python). The actual rules are a bit more complicated but the main message is clear: Do not use typed objects without knowing that they are not set to None. The main purpose of typing things as ndarray is to allow efficient indexing of single elements, and to speed up access to a small number of attributes such as .shape. Typing does not allow Cython to speed up mathematical operations on the whole array (for example, adding two arrays together).
🌐
Python Like You Mean It
pythonlikeyoumeanit.com › Module5_OddsAndEnds › Writing_Good_Code.html
Writing Good Code — Python Like You Mean It
Eventually, popular 3rd party libraries like NumPy will contribute their own typing modules so that you can provide higher-fidelity hints that indicate things like data-type and array-shape. NumPy developers are currently working on this. ... Python’s typing module contains objects that are used to create descriptive type-hints.
🌐
NumPy
numpy.org › doc › 2.3 › reference › typing.html
Typing (numpy.typing) — NumPy v2.3 Manual
The timedelta64 class is not considered a subclass of signedinteger, the former only inheriting from generic while static type checking. During runtime numpy aggressively casts any passed 0D arrays into their corresponding generic instance. Until the introduction of shape typing (see PEP 646) it is unfortunately not possible to make the necessary distinction between 0D and >0D arrays.
🌐
NumPy
numpy.org › doc › 2.1 › reference › generated › numpy.dtype.shape.html
numpy.dtype.shape — NumPy v2.1 Manual
Shape tuple of the sub-array if this data type describes a sub-array, and () otherwise.