mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[ty] More tests for TypedDict (#20205)
## Summary A small set of additional tests for `TypedDict` that I wrote while going through the spec. Note that this certainly doesn't make the test suite exhaustive (see remaining open points in the updated list here: https://github.com/astral-sh/ty/issues/154).
This commit is contained in:
parent
b49aa35074
commit
1aaa0847ab
1 changed files with 50 additions and 2 deletions
|
@ -128,6 +128,9 @@ Person({"name": "Alice"})
|
|||
accepts_person({"name": "Alice"})
|
||||
# TODO: this should be an error, similar to the above
|
||||
house.owner = {"name": "Alice"}
|
||||
a_person: Person
|
||||
# TODO: this should be an error, similar to the above
|
||||
a_person = {"name": "Alice"}
|
||||
```
|
||||
|
||||
All of these have an invalid type for the `name` field:
|
||||
|
@ -144,6 +147,9 @@ Person({"name": None, "age": 30})
|
|||
accepts_person({"name": None, "age": 30})
|
||||
# TODO: this should be an error, similar to the above
|
||||
house.owner = {"name": None, "age": 30}
|
||||
a_person: Person
|
||||
# TODO: this should be an error, similar to the above
|
||||
a_person = {"name": None, "age": 30}
|
||||
```
|
||||
|
||||
All of these have an extra field that is not defined in the `TypedDict`:
|
||||
|
@ -160,6 +166,9 @@ Person({"name": "Alice", "age": 30, "extra": True})
|
|||
accepts_person({"name": "Alice", "age": 30, "extra": True})
|
||||
# TODO: this should be an error
|
||||
house.owner = {"name": "Alice", "age": 30, "extra": True}
|
||||
# TODO: this should be an error
|
||||
a_person: Person
|
||||
a_person = {"name": "Alice", "age": 30, "extra": True}
|
||||
```
|
||||
|
||||
## Type ignore compatibility issues
|
||||
|
@ -242,8 +251,9 @@ invalid_extra = OptionalPerson(name="George", extra=True)
|
|||
|
||||
## `Required` and `NotRequired`
|
||||
|
||||
You can have fine-grained control over field requirements using `Required` and `NotRequired`
|
||||
qualifiers, which override the class-level `total=` setting:
|
||||
You can have fine-grained control over keys using `Required` and `NotRequired` qualifiers. These
|
||||
qualifiers override the class-level `total` setting, which sets the default (`total=True` means that
|
||||
all keys are required by default, `total=False` means that all keys are non-required by default):
|
||||
|
||||
```py
|
||||
from typing_extensions import TypedDict, Required, NotRequired
|
||||
|
@ -444,6 +454,12 @@ class Person(TypedDict, total=False):
|
|||
id: ReadOnly[Required[int]]
|
||||
name: str
|
||||
age: int | None
|
||||
|
||||
alice: Person = {"id": 1, "name": "Alice", "age": 30}
|
||||
alice["age"] = 31 # okay
|
||||
|
||||
# TODO: this should be an error
|
||||
alice["id"] = 2
|
||||
```
|
||||
|
||||
## Methods on `TypedDict`
|
||||
|
@ -764,6 +780,38 @@ from typing import TypedDict
|
|||
x: TypedDict = {"name": "Alice"}
|
||||
```
|
||||
|
||||
### `dict`-subclass inhabitants
|
||||
|
||||
Values that inhabit a `TypedDict` type must be instances of `dict` itself, not a subclass:
|
||||
|
||||
```py
|
||||
from typing import TypedDict
|
||||
|
||||
class MyDict(dict):
|
||||
pass
|
||||
|
||||
class Person(TypedDict):
|
||||
name: str
|
||||
age: int | None
|
||||
|
||||
# TODO: this should be an error
|
||||
x: Person = MyDict({"name": "Alice", "age": 30})
|
||||
```
|
||||
|
||||
### Cannot be used in `isinstance` tests
|
||||
|
||||
```py
|
||||
from typing import TypedDict
|
||||
|
||||
class Person(TypedDict):
|
||||
name: str
|
||||
age: int | None
|
||||
|
||||
def _(obj: object) -> bool:
|
||||
# TODO: this should be an error
|
||||
return isinstance(obj, Person)
|
||||
```
|
||||
|
||||
## Diagnostics
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue