With the code
from typing import overload
import numpy as np
import numpy.typing as npt
class BoundedArray:
@overload
def __init__(
self,
array: npt.ArrayLike,
*,
upper_bounds: npt.ArrayLike,
lower_bounds: npt.ArrayLike | None,
) -> None:
...
@overload
def __init__(
self,
array: npt.ArrayLike,
*,
upper_bounds: npt.ArrayLike | None,
lower_bounds: npt.ArrayLike,
) -> None:
...
def __init__(self, array, *, upper_bounds=None, lower_bounds=0.0):
self.array = array
self.upper_bounds = upper_bounds
self.lower_bounds = lower_bounds
self.clip()
def clip(self) -> None:
if self.upper_bounds is None and self.lower_bounds is None:
return
np.clip(self.array, self.lower_bounds, self.upper_bounds, out=self.array)
I get the type-checking error
> Overloaded implementation is not consistent with signature of overload 1
>> Type "(self: Self@BoundedArray, array: Unknown, *, upper_bounds: Unknown | None = None, lower_bounds: float = 0) -> None" cannot be assigned to type "(self: Self@BoundedArray, array: ArrayLike, *, upper_bounds: ArrayLike, lower_bounds: ArrayLike | None) -> None"
>>> Keyword parameter "lower_bounds" of type "ArrayLike" cannot be assigned to type "float"
>>>> Type "ArrayLike" cannot be assigned to type "float"
>>>>> "_SupportsArray[dtype[Unknown]]" is incompatible with "float"
According to the documentation of overload the actual implementation should be ignored by the type-checker which apparently is not the case here. Taking this as a given, simply annotating the actual implementation with
def __init__(
self,
array: npt.ArrayLike,
*,
upper_bounds: npt.ArrayLike | None,
lower_bounds: npt.ArrayLike | None,
) -> None:
to rectify the falsely inferred "float" type does not seem to do the trick, as it leads to the errors
> Argument of type "ArrayLike | None" cannot be assigned to parameter "a_min" of type "None" in function "clip"
> Argument of type "ArrayLike | None" cannot be assigned to parameter "a_max" of type "ArrayLike" in function "clip"
even though I specifically try to prevent that with the type guard.
The only way I have found to not get any errors is annotating the implementation like
def __init__(
self,
array,
*,
upper_bounds=None,
lower_bounds: npt.ArrayLike | None = 0.0,
):
but it seems to be quite inconsistent to me (only annotating a single variable). Am I missing anything, is this just a buggy implementation, is there a different solution or is it supposed to work this way?