Skip to content

Commit

Permalink
Removed the requirement that a TypedDict be marked @final for it …
Browse files Browse the repository at this point in the history
…to be considered for type narrowing as part of a `S in D` type guard pattern. This requirement wasn't sound because TypedDict is a structural type (i.e. a protocol), so `@final` doesn't have any real meaning.
  • Loading branch information
msfterictraut committed Jul 17, 2023
1 parent 6169e0f commit 6a25a7b
Show file tree
Hide file tree
Showing 3 changed files with 3 additions and 22 deletions.
2 changes: 1 addition & 1 deletion docs/type-concepts-advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ In addition to assignment-based type narrowing, Pyright supports the following t
* `x[I] is None` and `x[I] is not None` (where I is a literal expression and x is a known-length tuple that is distinguished by the index indicated by I)
* `len(x) == L` and `len(x) != L` (where x is tuple and L is a literal integer)
* `x in y` or `x not in y` (where y is instance of list, set, frozenset, deque, tuple, dict, defaultdict, or OrderedDict)
* `S in D` and `S not in D` (where S is a string literal and D is a final TypedDict)
* `S in D` and `S not in D` (where S is a string literal and D is a TypedDict)
* `isinstance(x, T)` (where T is a type or a tuple of types)
* `issubclass(x, T)` (where T is a type or a tuple of types)
* `callable(x)`
Expand Down
6 changes: 1 addition & 5 deletions packages/pyright-internal/src/analyzer/typeGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1816,11 +1816,7 @@ function narrowTypeForTypedDictKey(

if (isPositiveTest) {
if (!tdEntry) {
// If the class is final, we can say with certainty that if
// the TypedDict doesn't define this entry, it is not this type.
// If it's not final, we can't say this because it could be a
// subclass of this TypedDict that adds more fields.
return ClassType.isFinal(subtype) ? undefined : subtype;
return undefined;
}

// If the entry is currently not required and not marked provided, we can mark
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
# This sample tests type narrowing for TypedDict types based
# on whether a key is in or not in the dict.

from typing import TypedDict, final
from typing import TypedDict


@final
class TD1(TypedDict):
a: str
b: int


@final
class TD2(TypedDict):
a: int
c: str


@final
class TD3(TypedDict, total=False):
a: int
d: str


class TD4(TypedDict):
a: int
c: str


def f1(p: TD1 | TD2):
if "b" in p:
reveal_type(p, expected_text="TD1")
Expand Down Expand Up @@ -91,10 +83,3 @@ def f7(p: TD3):
def f8(p: TD3):
if "a" in p:
f7(p)


def f9(p: TD1 | TD4):
if "b" in p:
reveal_type(p, expected_text="TD1 | TD4")
else:
reveal_type(p, expected_text="TD4")

0 comments on commit 6a25a7b

Please sign in to comment.