The problem is that mypy inferred that the type of your result
variable is Dict[str, bool]
due to how you first initialized it on line 2.
Consequently, when you try and insert a str later, mypy (rightfully) complains. You have several options for fixing your code, which I'll list in order of least to most type-safe.
Option 1 is to declare your dictionary such that its values are of type Any
-- that is, your values will not be type-checked at all:
from typing import Any, Dict
def my_func(condition: bool) -> Dict[str, Any]:
result = {"success": False} # type: Dict[str, Any]
if condition:
result["success"] = True
else:
result["message"] = "error message"
return result
Note that we needed to annotate your second line to give mypy a hint about what the type of result
should be, to help its inference process.
If you're using Python 3.6+, you can annotate that line using the following alternate syntax, which uses variable annotations (which are new as of Python 3.6):
result: Dict[str, Any] = {"success": False}
Option 2 is slightly more type-safe -- declare your values to be either strs or bools, but nothing else, using Union
. This isn't fully typesafe, but at least you can still have some checks on your dict.
from typing import Any, Dict
def my_func(condition: bool) -> Dict[str, Union[str, bool]]:
result = {"success": False} # type: Dict[str, Union[str, bool]]
if condition:
result["success"] = True
else:
result["message"] = "error message"
return result
You may perhaps find that type annotation to be a little long/annoying to type, so you could use type aliases for readability (and optionally use the variable annotation syntax), like so:
ResultJson = Dict[str, Union[str, bool]]
def my_func(condition: bool) -> ResultJson
result: ResultJson = {"success": False}
# ...snip...
If you are ok with dropping compatibility with older versions of Python, you can shrink your alias slightly more by using some new syntax introduced in Python 3.10:
# Python 3.10 lets you use 'dict' instead of Dict
ResultJson = dict[str, str | bool]
def my_func(condition: bool) -> ResultJson
result: ResultJson = {"success": False}
# ...snip...
Option 3 is the most type-safe: you can assign specific types to different fields in your dict using a TypedDict:
from typing import Optional, TypedDict
# You can also use the older:
#
# ResultJson = TypedDict("ResultJson", {...})
#
# ...syntax if you need to support versions of Python
# older then 3.6 or need to support keys that are not
# valid Python identifiers.
class ResultJson(TypedDict):
success: bool
message: Optional[str]
def my_func(condition: bool) -> ResultJson:
result: ResultJson = {"success": False, "message": None}
if condition:
result["success"] = True
else:
result["message"] = "error message"
return result
The main caveat is that typed_dict was added in Python 3.8. If you need to support older versions of Python, pip-install the typing_extensions
package and use from typing_extensions import TypedDict
instead.