2

I am trying to create a few functions which will return values of different TypedDict types. Most of fields in them will be same so I want to generate base dictionary with same function in all cases. However I am getting stumped by typing this correctly.

My idea was to create base type Parent and inherit from it, adding only NotRequired fields.

from typing_extensions import NotRequired, TypedDict

class Parent(TypedDict):
    parent_field: str


class Child(Parent):
    child_field: NotRequired[bool | None]


def create_parent() -> Parent:
    return {"parent_field": "example"}


child: Child = create_parent()
# Error:
# Expression of type "Parent" cannot be assigned to declared type "Child"
#  "child_field" is missing from "Type[Parent]"

However this fails since field child_field is missing, even though its type is NotRequired. Why it fails and how to evade this problem?

EDIT: I am using pylance (so pyright) for typechecking.

mypy (playground) gives similar error message:

Incompatible types in assignment (expression has type "Parent", variable has type "Child") [assignment]

STerliakov
  • 4,983
  • 3
  • 15
  • 37
Matija Sirk
  • 596
  • 2
  • 15
  • 1
    What this code means? `child` is not a `Parent` or `Child`. `child` is a `dict`. `create_parent` also returning `dict`. Finally, I checked this code with `python 3.10.2` and no error happens. – rzlvmp Jan 27 '23 at 08:41
  • 1
    @rzlvmp OP means a `mypy` error (and should mention that in his question). And you should check out [PEP589](https://peps.python.org/pep-0589/), then you might understand the question. – Daniil Fajnberg Jan 27 '23 at 08:47
  • @DaniilFajnberg I expected `mypy` here. But `mypy` also returning different error: `error: Incompatible types in assignment (expression has type "Parent", variable has type "Child")`. And this error is more reasonable. – rzlvmp Jan 27 '23 at 08:52
  • I am using pylance, sorry for not mentioning - will edit question. – Matija Sirk Jan 27 '23 at 09:11

1 Answers1

2

This question is explained in PEP589 explicitly. Let me quote:

A TypedDict type A with no key x is not consistent with a TypedDict type with a non-required key x, since at runtime the key x could be present and have an incompatible type (which may not be visible through A due to structural subtyping). Example:

class A(TypedDict, total=False):
    x: int
    y: int

class B(TypedDict, total=False):
    x: int

class C(TypedDict, total=False):
    x: int
    y: str

 def f(a: A) -> None:
     a['y'] = 1

 def g(b: B) -> None:
     f(b)  # Type check error: 'B' incompatible with 'A'

 c: C = {'x': 0, 'y': 'foo'}
 g(c)
 c['y'] + 'bar'  # Runtime error: int + str

So, your Parent class is not assignable to variable of type Child, and pylance points it out.

STerliakov
  • 4,983
  • 3
  • 15
  • 37