Should total=False mean all keys are NotRequired by default?

Currently, when retrieving TypedDict.__annotations__ with a base that has total=False, you need to compare with __optional_keys__ to ensure the keys are not required. Since total=False means that keys are not required by default, does it make sense to annotate them as such beforehand? Otherwise __annotations__ ends up with keys that are annotated as NotRequired and Required as well as keys without NotRequired from total=False, which seems odd?

Semantically total=False means the same as annotating every key with NotRequired.

Are you suggesting that __annotations__ should be transformed automatically to add NotRequired[] to every key in that case? I would oppose that; in my experience, it’s generally better to keep processing of things like __annotations__ to a minimum at runtime. That way, it’s easier for any tools that want to know exactly what the user wrote. For example, if the runtime has a bug and puts the wrong thing in __annotations__, such tools might not know what to do any more.

1 Like

In that case, would you expect get_type_hints to return the total=False keys annotated correctly?

I think there’s a difference there between what I expect and what I want. I expect get_type_hints() do all kinds of weird things to the types (turning None into NoneType?). I want it to not do that. Therefore, I have learned to avoid using it.

My main concern now is that if we change get_type_hints(), that’s likely to break some users. Those users might then get upset at me as I’m one of the maintainers of, and I don’t like it when users get upset at me. Also, if we make get_type_hints() more complicated, that makes it harder to maintain in the future.


Come to think of it, is there a formal reason for why None becomes type(None)? I went sleuthing around for the commit history and didn’t find anything definitive for this behaviour.

None is such a common annotation we made an exception for it in the syntax, letting users write x: None instead of x: NoneType.

But we assumed that this irregularity would be annoying when inspecting annotations at runtime – it’d be more consistent if the runtime types to always be instances of type. I think we were wrong there, and runtime introspectors are more likely to be surprised than delighted by this choice. But it’s too late to change.


Currently, when retrieving annotations, I’m finding it necessary to cross-reference with __optional_keys__ to ensure keys aren’t considered required. The challenge here is that it results in a mix of keys annotated as NotRequired, Required, and those without NotRequired from total=False .