import typing as t
class Data[T]:
pass
class DataCollection:
a: Data[str]
def foo[T](self, data: Data[T], elem: T) -> T:
... # type: ignore
t.reveal_type(DataCollection().foo(DataCollection.a, 1))
The revealed type here is str | int, when (in my opinion and I don’t see why it wouildn’t) it should just give me an error that 1 is not valid for the type str that is already given.
Is this an error or intended?
Whatever might be the case, is there a solution/workaround?
This might be unexpected, but it is working as intended. Consider this example from PEP 483:
def do_nothing(one_arg: T, other_arg: T) -> None:
pass
do_nothing(1, 2) # OK, T is int
do_nothing('abc', UserID(42)) # also OK, T is object
When inferring the type of T, the type checker is allowed to “widen” the type to find a solution.
Depending on what you’re trying to do exactly, maybe it is possible to make DataCollection generic over T, for example:
class Data[T]:
pass
class DataCollection[T]:
a: Data[T]
def foo(self, data: Data[T], elem: T) -> T:
... # type: ignore
dc = DataCollection[str]()
dc.foo(dc.a, 1) # error: Argument of type "Literal[1]" cannot be assigned to parameter "elem" of type "str" in function "foo"
Well I am basically giving it another try to build my own, small, custom ORM.
So DataCollection is just Task here and the function foo is the insert function in the BaseModel class, where I want to pass tuples of the column and its data.
class Column[T]:
...
class BaseModel(metaclass=_BaseModelMeta):
def insert[T](self, col1: Column[T], data1: T):
...
class Task(BaseModel):
name: Column[str]
difficulty: Column[int]
I kind of found a workaround by adding a “bind” function to Column that just returns me a ColumnInsert object:
class ColumnInsert[T](t.NamedTuple):
column: Column[T]
insert: T
class Column[T]:
def bind(self, insert: T, /) -> ColumnInsert[T]:
...
class BaseModel:
def insert(self, column_insert: ColumnInsert) -> Insert:
...
class Task(BaseModel):
name: Column[str]
difficulty: Column[int]
task = Task({'name': "hi", 'difficulty': 10})
x = task.insert(Task.name.bind("a"))
This works but is an annoying workaround that pollutes my function call, so I would prefer the other way, which didn’t work.