You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a field name is being used to narrow a union of TypedDict types, this narrowing does not seem to consider derived TypedDict classes, resulting in a union of types incorrectly narrowed to one of the types in that union.
Code or Screenshots
fromtypingimportTypedDictclassX(TypedDict):
a: intclassX1(X):
b: strclassX2(X):
c: strdeffn1(x: X1) ->None:
passdeffn2(x: X2) ->None:
passdeffn(x: X1|X2) ->None:
if"b"inx:
reveal_type(x) # Type of "x" is "X1"fn1(x) # should be an error because x is X1|X2elif"c"inx:
reveal_type(x) # Type of "x" is "X2"fn2(x)
else:
reveal_type(x) # unreachable coderaiseValueError()
The same code in MyPy fails when calling fn1 because x is still X1|X2 at that point (i.e. a class derived from X2 may have a b field). Adding @final to X1 and X2 in MyPy makes the error go away.
The text was updated successfully, but these errors were encountered:
A TypedDict defines a structural type, similar to a Protocol. It doesn't make sense to mark a structural type definition as @final because inheritance is not used when determining type compatibility with a structural type. The fact that mypy pays attention to @final in this case is incorrect from a typing standpoint.
The type narrowing pattern here allows for a discriminated union between two or more typed dictionaries. You're correct in pointing out that it does allow for a soundness hole, but this pattern is sufficiently common — and useful — that the tradeoff is worth it.
We may be able to close this hole if and when PEP 728 is approved and the usage of "closed" TypedDicts becomes more common. In the meantime, the behavior you're observing here is the best we can do.
I'm not advocating for @final, just saying that today using @final is how MyPy knows that presence of b is sufficient to narrow the union to a single type. In fact, I cannot use @final for very reason you are describing in that such classes cannot be returned from @abstractmethod overrides with concrete return types.
It sounds like closed will replace @final in this in MyPy, so this is good. Just have to wait for a few years, I suppose. Thanks for the reference.
Describe the bug
When a field name is being used to narrow a union of
TypedDict
types, this narrowing does not seem to consider derivedTypedDict
classes, resulting in a union of types incorrectly narrowed to one of the types in that union.Code or Screenshots
The same code in MyPy fails when calling
fn1
becausex
is stillX1|X2
at that point (i.e. a class derived fromX2
may have ab
field). Adding@final
toX1
andX2
in MyPy makes the error go away.The text was updated successfully, but these errors were encountered: