-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[ty] New Type variant for TypedDict
#19733
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
58bddcd
bf06e37
fd59f0f
d93a964
933cd7a
ca2c229
6030703
4f135f3
4625199
6c7dada
2b01713
f58ed02
3a91929
a2077a2
889920f
9968d82
bfa22e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,13 +60,17 @@ Assignments to keys are also validated: | |
| ```py | ||
| # TODO: this should be an error | ||
| alice["name"] = None | ||
| # TODO: this should be an error | ||
| bob["name"] = None | ||
sharkdp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| Assignments to non-existing keys are disallowed: | ||
|
|
||
| ```py | ||
| # TODO: this should be an error | ||
| alice["extra"] = True | ||
| # TODO: this should be an error | ||
| bob["extra"] = True | ||
| ``` | ||
|
|
||
| ## Structural assignability | ||
|
|
@@ -123,7 +127,7 @@ dangerous(alice) | |
| reveal_type(alice["name"]) # revealed: Unknown | ||
| ``` | ||
|
|
||
| ## Types of keys and values | ||
| ## Methods on `TypedDict` | ||
|
|
||
| ```py | ||
| from typing import TypedDict | ||
|
|
@@ -133,8 +137,13 @@ class Person(TypedDict): | |
| age: int | None | ||
|
|
||
| def _(p: Person) -> None: | ||
| reveal_type(p.keys()) # revealed: @Todo(Support for `TypedDict`) | ||
| reveal_type(p.values()) # revealed: @Todo(Support for `TypedDict`) | ||
| reveal_type(p.keys()) # revealed: dict_keys[str, object] | ||
| reveal_type(p.values()) # revealed: dict_values[str, object] | ||
|
|
||
| reveal_type(p.setdefault("name", "Alice")) # revealed: @Todo(Support for `TypedDict`) | ||
|
|
||
| reveal_type(p.get("name")) # revealed: @Todo(Support for `TypedDict`) | ||
| reveal_type(p.get("name", "Unknown")) # revealed: @Todo(Support for `TypedDict`) | ||
| ``` | ||
|
|
||
| ## Unlike normal classes | ||
|
|
@@ -149,11 +158,16 @@ class Person(TypedDict): | |
| name: str | ||
| age: int | None | ||
|
|
||
| # TODO: this should be an error | ||
| # error: [unresolved-attribute] "Type `<class 'Person'>` has no attribute `name`" | ||
| Person.name | ||
|
|
||
| # TODO: this should be an error | ||
| Person(name="Alice", age=30).name | ||
sharkdp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| def _(P: type[Person]): | ||
| # error: [unresolved-attribute] "Type `type[Person]` has no attribute `name`" | ||
| P.name | ||
|
|
||
| def _(p: Person) -> None: | ||
| # error: [unresolved-attribute] "Type `Person` has no attribute `name`" | ||
| p.name | ||
| ``` | ||
|
|
||
| ## Special properties | ||
|
|
@@ -167,9 +181,29 @@ class Person(TypedDict): | |
| name: str | ||
| age: int | None | ||
|
|
||
| reveal_type(Person.__total__) # revealed: @Todo(Support for `TypedDict`) | ||
| reveal_type(Person.__required_keys__) # revealed: @Todo(Support for `TypedDict`) | ||
| reveal_type(Person.__optional_keys__) # revealed: @Todo(Support for `TypedDict`) | ||
| reveal_type(Person.__total__) # revealed: bool | ||
| reveal_type(Person.__required_keys__) # revealed: frozenset[str] | ||
| reveal_type(Person.__optional_keys__) # revealed: frozenset[str] | ||
|
Comment on lines
+184
to
+186
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it worth also adding some tests that demonstrate that these attributes cannot be accessed from inhabitants of the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, thanks. I added a test, but it fails (added a TODO). No other type checker gets this right, so I'm not going to invest time right now. |
||
| ``` | ||
|
|
||
| These attributes can not be accessed on inhabitants: | ||
|
|
||
| ```py | ||
| def _(person: Person) -> None: | ||
| # TODO: these should be errors | ||
| person.__total__ | ||
| person.__required_keys__ | ||
| person.__optional_keys__ | ||
| ``` | ||
|
|
||
| Also, they can not be accessed on `type(person)`, as that would be `dict` at runtime: | ||
|
|
||
| ```py | ||
| def _(t_person: type[Person]) -> None: | ||
| # TODO: these should be errors | ||
| t_person.__total__ | ||
| t_person.__required_keys__ | ||
| t_person.__optional_keys__ | ||
| ``` | ||
|
|
||
| ## Subclassing | ||
|
|
@@ -272,6 +306,9 @@ msg = Message(id=1, content="Hello") | |
| OtherMessage = TypedDict("OtherMessage", {"id": int, "content": str}, closed=True) | ||
|
|
||
| reveal_type(Message.__required_keys__) # revealed: @Todo(Support for `TypedDict`) | ||
|
|
||
| # TODO: this should be an error | ||
| msg.content | ||
sharkdp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| [`typeddict`]: https://typing.python.org/en/latest/spec/typeddict.html | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you maybe deleted the
### Unionsheader above accidentally hereThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah. I noticed that to independently and fixed it in #19758