I am using pydantic models generated from typeddicts in a project with mypy but I get a invalid type
error when using one of those models as a return type.
This does not happend when directly using a BaseModel.
from pydantic import create_model_from_typeddict
from typing import TypedDict
class TD_Foo(TypedDict):
bar: int
M_Foo = create_model_from_typeddict(TD_Foo)
# error: Variable "pls.M_Foo" is not valid as a type [valid-type]
def make_foo() -> M_Foo:
return M_Foo(bar=42)
print(make_foo())
Is there an issue with my code or should I raise an issue ? Also if anyone knows if I should raise it on mypy or pydantic's repo ?
Looking at the source, it appears create_model_from_typeddict
calls regular old create_model
under the hood and passes along all **kwargs
to it.
The function create_model
by default always returns the type Type[BaseModel]
. This is also how the return type of create_model_from_typeddict
is annotated. Thus, it will definitely be correct in your case to annotate make_foo
accordingly:
from pydantic import BaseModel, create_model_from_typeddict
from typing import TypedDict
class TD_Foo(TypedDict):
bar: int
M_Foo = create_model_from_typeddict(TD_Foo)
def make_foo() -> BaseModel:
return M_Foo(bar=42)
No issues with mypy
.
What I have an issue with, now that I am looking at this, is that create_model_from_typeddict
doesn't mirror the overloaded annotations of create_model
and thus the following causes a type error:
from pydantic import BaseModel, create_model_from_typeddict from typing import TypedDict
class TD_Foo(TypedDict):
bar: int
class FooModel(BaseModel):
pass
M_Foo = create_model_from_typeddict(
TD_Foo,
__base__=FooModel,
)
def make_foo() -> FooModel:
return M_Foo(bar=42)
Mypy correctly picks up the following:
20: error: Incompatible return value type (got "BaseModel", expected "FooModel") [return-value]
Actually the type returned indeed is FooModel
because M_Foo
is in fact Type[FooModel]
. This is, in my opinion, due to create_model_from_typeddict
being wrongly (or incompletely) annotated. If passed a base class, the return type should be inferred accordingly, as it does with create_model
.
I think I'll create a Pull Request for this, once I verify this is warranted.
Anyway, I hope this helps.
In case you are still wondering, why you can't just use M_Foo
as an annotation, that is due to that class being dynamically created at runtime. It is just the old dilemma between static type checking and dynamic typing. Generally speaking, the type checker will not be able to handle such types.