Reputation: 13507
The code below defines a TypedDict
with optional keys.
The if
statement in the middle raises an exception if a certain key is absent, which means that the next lines will be executed only if that key is present. It seems Pylance can't figure out this logic automatically. How am I supposed to handle this? Reassign a new typing to the same Dict
somehow?
output_dict_type = TypedDict(
'some description',
{
'id': str
'name': str
},
total=False
)
my_dict:output_dict_type = {
'id': 'foo'
}
if not my_dict.get('name'):
raise Exception('name is required')
if my_dict['name'] == 'John': # type error: "name" is not a required key...
do_stuff()
Upvotes: 0
Views: 1560
Reputation: 40683
This might be a good place to practice "asking for forgiveness is easier then seeking permission". By which I mean wrapping the key fetch in a try/except block. If the key isn't present then throw an exception, and the code that follows later will be guaranteed that the fetched key exists.
from typing import Optional, TypedDict
class OutputType(TypedDict, total=False):
id: str
name: Optional[str]
my_dict: OutputType = ...
# guards
try:
name = my_dict['name']
except KeyError:
# name local var not initialised here
raise Exception('name is required')
# name local var guaranteed to be initialised here
# since you cannot guarantee that name will be a str, we still have
# to do a type check
if not isinstance(name, str):
raise Exception('name must be str')
# happy path code
if name == 'John':
...
If you cannot rely upon your input data being in precise format, then you should write some custom derserialisation logic. This logic can be tucked away in another function and can handle all the type checking and other validation. It can then return a dataclass which is fully typed and you can just get on with processing the data.
from dataclasses import dataclass
@dataclass
class OutputType:
id: str
name: str
@classmethod
def deserialise(cls, id=None, name=None) -> 'OutputType':
assert isinstance(id, str), 'id must be present and a string'
assert isinstance(name, str), 'name must be present and a string'
return Output(id, name)
output_dict: dict[str, Any] = ...
output = OutputType.deserialise(**output_dict)
if output.name == 'John':
...
Upvotes: 1