Repository: yukinarit/pyserde
Pull request #503 (2024-03-23 14:02:01)

Drop python 3.8 and use PEP585 entirely


Patch:
diff --git a/README.md b/README.md
index e1ca35e..ac9122b 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,6 @@ Declare a class with pyserde's `@serde` decorator.
 
 ```python
 @serde
-@dataclass
 class Foo:
     i: int
     s: str
@@ -58,8 +57,8 @@ Foo(i=10, s='foo', f=100.0, b=True)
 - Supported types
     - Primitives (`int`, `float`, `str`, `bool`)
     - Containers
-        - `List`, `Set`, `Tuple`, `Dict`
-        - [`FrozenSet`](https://docs.python.org/3/library/stdtypes.html#frozenset), [`DefaultDict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)
+        - `list`, `set`, `tuple`, `dict`
+        - [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset), [`defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)
     - [`typing.Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)
     - [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union)
     - User defined class with [`@dataclass`](https://docs.python.org/3/library/dataclasses.html)
diff --git a/bench/attrs_class.py b/bench/attrs_class.py
index 030ce6c..2519c32 100644
--- a/bench/attrs_class.py
+++ b/bench/attrs_class.py
@@ -1,6 +1,6 @@
 import json
 from functools import partial
-from typing import List, Union
+from typing import Union
 
 import attr
 import data
@@ -17,7 +17,7 @@ class Small:
 
 @attr.s(auto_attribs=True)
 class Medium:
-    inner: List[Small] = attr.Factory(list)
+    inner: list[Small] = attr.Factory(list)
 
 
 SMALL = Small(**data.args_sm)
diff --git a/docs/en/class-attributes.md b/docs/en/class-attributes.md
index ea39038..eeebf01 100644
--- a/docs/en/class-attributes.md
+++ b/docs/en/class-attributes.md
@@ -51,7 +51,6 @@ print(to_json(f))
 >
 > ```python
 > @serde(rename_all = 'camelcase')
-> @dataclass
 > class Foo:
 >     int_field: int
 >     str_field: str = field(rename='str-field')
@@ -86,7 +85,6 @@ class MyDeserializer:
         return datetime.strptime(value, "%d/%m/%y")
 
 @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
-@dataclass
 class Foo:
     v: datetime
 ```
@@ -143,7 +141,6 @@ def deserializer(cls, o):
         raise SerdeSkip()
 
 @serde(serializer=serializer, deserializer=deserializer)
-@dataclass
 class Foo:
     a: datetime
 ```
@@ -160,7 +157,6 @@ New in v0.9.8. Since `dataclasses.fields` doesn't include a class variable [^1],
 
 ```python
 @serde(serialize_class_var=True)
-@dataclass
 class Foo:
     a: ClassVar[int] = 10
 ```
diff --git a/docs/en/field-attributes.md b/docs/en/field-attributes.md
index 5b1bb12..f1378bc 100644
--- a/docs/en/field-attributes.md
+++ b/docs/en/field-attributes.md
@@ -12,7 +12,7 @@ Field attributes are options to customize (de)serialization behaviour for a fiel
 class Foo:
     a: int = 10
     b: int = field(default=10)  # same as field "a"
-    c: Dict[str, int] = field(default_factory=dict)
+    c: dict[str, int] = field(default_factory=dict)
 
 print(from_dict(Foo, {}))  # prints Foo(a=10, b=10, c={})
 ```
@@ -32,7 +32,6 @@ Here is an example specifying `rename` attribute in both `serde.field` and `data
 
 ```python
 @serde.serde
-@dataclass.dataclass
 class Foo:
     a: str = serde.field(rename="A")
     b: str = dataclasses.field(metadata={"serde_rename"="B"})
@@ -44,7 +43,6 @@ class Foo:
 
 ```python
 @serde
-@dataclass
 class Foo:
     id: int = field(rename="ID")
 ```
@@ -57,11 +55,10 @@ See [examples/rename.py](https://github.com/yukinarit/pyserde/blob/main/examples
 
 ```python
 @serde
-@dataclass
 class Resource:
     name: str
     hash: str
-    metadata: Dict[str, str] = field(default_factory=dict, skip=True)
+    metadata: dict[str, str] = field(default_factory=dict, skip=True)
 ```
 
 See [examples/skip.py](https://github.com/yukinarit/pyserde/blob/main/examples/skip.py) for the complete example.
@@ -72,7 +69,6 @@ See [examples/skip.py](https://github.com/yukinarit/pyserde/blob/main/examples/s
 
 ```python
 @serde
-@dataclass
 class World:
     buddy: str = field(default='', skip_if=lambda v: v == 'Pikachu')
 ```
@@ -85,9 +81,8 @@ See [examples/skip.py](https://github.com/yukinarit/pyserde/blob/main/examples/s
 
 ```python
 @serde
-@dataclass
 class World:
-    enemies: List[str] = field(default_factory=list, skip_if_false=True)
+    enemies: list[str] = field(default_factory=list, skip_if_false=True)
 ```
 
 See [examples/skip.py](https://github.com/yukinarit/pyserde/blob/main/examples/skip.py) for the complete example.
@@ -98,7 +93,6 @@ See [examples/skip.py](https://github.com/yukinarit/pyserde/blob/main/examples/s
 
 ```python
 @serde
-@dataclass
 class World:
     town: str = field(default='Masara Town', skip_if_default=True)
 ```
@@ -111,7 +105,6 @@ You can set aliases for field names. Alias only works for deserialization.
 
 ```python
 @serde
-@dataclass
 class Foo:
     a: str = field(alias=["b", "c"])
 ```
@@ -130,7 +123,6 @@ In the following example, field `a` is serialized into `"2021-01-01T00:00:00"` b
 
 ```python
 @serde
-@dataclass
 class Foo:
     a: datetime
     b: datetime = field(serializer=lambda x: x.strftime('%d/%m/%y'), deserializer=lambda x: datetime.strptime(x, '%d/%m/%y'))
@@ -144,13 +136,11 @@ You can flatten the fields of the nested structure.
 
 ```python
 @serde
-@dataclass
 class Bar:
     c: float
     d: bool
 
 @serde
-@dataclass
 class Foo:
     a: int
     b: str
diff --git a/docs/en/getting-started.md b/docs/en/getting-started.md
index b427873..778437f 100644
--- a/docs/en/getting-started.md
+++ b/docs/en/getting-started.md
@@ -2,7 +2,7 @@
 
 ## Installation
 
-Install pyserde from PyPI. pyserde requires Python>=3.8.
+Install pyserde from PyPI. pyserde requires Python>=3.9.
 
 ```
 pip install pyserde
@@ -48,11 +48,9 @@ Here are the available extras
 Define your class with pyserde's `@serde` decorators. Be careful that module name is `serde`, not `pyserde`. `pyserde` heavily depends on the standard library's `dataclasses` module. If you are new to dataclass, I would recommend to read [dataclasses documentation](https://docs.python.org/3/library/dataclasses.html) first.
 
 ```python
-from dataclasses import dataclass
 from serde import serde
 
 @serde
-@dataclass
 class Foo:
     i: int
     s: str
@@ -66,11 +64,9 @@ pyserde generates methods necessary for (de)serialization by `@serde` when a cla
 >
 > e.g. If you do only serialization, you can use `@serialize` decorator. But calling deserialize API e.g. `from_json` for `Foo` will raise an error.
 > ```python
-> from dataclasses import dataclass
 > from serde import serialize
 >
 > @serialize
-> @dataclass
 > class Foo:
 >     i: int
 >     s: str
@@ -83,7 +79,6 @@ pyserde generates methods necessary for (de)serialization by `@serde` when a cla
 [PEP585](https://www.python.org/dev/peps/pep-0585/) style annotation is supported for python>=3.9. [PEP604](https://www.python.org/dev/peps/pep-0604/) Union operator is also supported for python>=3.10. With PEP585 and PEP604, you can write a pyserde class pretty neatly.
 ```python
 @serde
-@dataclass
 class Foo:
     a: int
     b: list[str]
diff --git a/docs/en/types.md b/docs/en/types.md
index 48b65fa..01aca34 100644
--- a/docs/en/types.md
+++ b/docs/en/types.md
@@ -4,8 +4,8 @@ Here is the list of the supported types. See the simple example for each type in
 
 * Primitives (int, float, str, bool) [^1]
 * Containers
-    * `List`, `Set`, `Tuple`, `Dict` [^2]
-    * [`FrozenSet`](https://docs.python.org/3/library/stdtypes.html#frozenset), [^3] [`DefaultDict`](https://docs.python.org/3/library/collections.html#collections.defaultdict) [^4]
+    * `list`, `set`, `tuple`, `dict` [^2]
+    * [`frozenset`](https://docs.python.org/3/library/stdtypes.html#frozenset), [^3] [`defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict) [^4]
 * [`typing.Optional`](https://docs.python.org/3/library/typing.html#typing.Optional) [^5]
 * [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union) [^6] [^7] [^8]
 * User defined class with [`@dataclass`](https://docs.python.org/3/library/dataclasses.html) [^9] [^10]
@@ -28,12 +28,10 @@ Here is the list of the supported types. See the simple example for each type in
 You can write pretty complex class like this:
 ```python
 @serde
-@dataclass
 class bar:
     i: int
 
 @serde
-@dataclass
 class Foo:
     a: int
     b: list[str]
diff --git a/docs/en/union.md b/docs/en/union.md
index 870489d..54b2d78 100644
--- a/docs/en/union.md
+++ b/docs/en/union.md
@@ -8,17 +8,14 @@ This is the default Union representation for pyserde<0.7. Given these dataclasse
 
 ```python
 @serde
-@dataclass
 class Bar:
     b: int
 
 @serde
-@dataclass
 class Baz:
     b: int
 
 @serde(tagging=Untagged)
-@dataclass
 class Foo:
     a: Union[Bar, Baz]
 ```
@@ -31,7 +28,6 @@ This is the default Union representation since 0.7. A class declaration with `Ex
 
 ```python
 @serde(tagging=ExternalTagging)
-@dataclass
 class Foo:
     a: Union[Bar, Baz]
 ```
@@ -42,7 +38,7 @@ class Foo:
 > @serde(tagging=ExternalTagging)
 > @dataclass
 > class Foo:
->    a: Union[List[int], Set[int]]
+>    a: Union[list[int], set[int]]
 > ```
 
 ## `InternalTagging`
@@ -51,7 +47,6 @@ A class declaration with `InternalTagging` looks like below. If you serialize `F
 
 ```python
 @serde(tagging=InternalTagging("type"))
-@dataclass
 class Foo:
     a: Union[Bar, Baz]
 ```
@@ -62,7 +57,6 @@ A class declaration with `AdjacentTagging` looks like below. If you serialize `F
 
 ```python
 @serde(tagging=AdjacentTagging("type", "content"))
-@dataclass
 class Foo:
     a: Union[Bar, Baz]
 ```
@@ -75,12 +69,10 @@ Passing Union types directly in (de)serialize APIs (e.g. to_json, from_json) was
 
 ```python
 @serde
-@dataclass
 class Foo:
     a: int
 
 @serde
-@dataclass
 class Bar:
     a: int
 
@@ -96,12 +88,10 @@ Since v0.12.0, pyserde can handle union that's passed in (de)serialize APIs a bi
 
 ```python
 @serde
-@dataclass
 class Foo:
     a: int
 
 @serde
-@dataclass
 class Bar:
     a: int
 
diff --git a/examples/alias.py b/examples/alias.py
index 4b5d77f..321d221 100644
--- a/examples/alias.py
+++ b/examples/alias.py
@@ -1,11 +1,8 @@
-from dataclasses import dataclass
-
 from serde import field, serde
 from serde.json import from_json
 
 
 @serde
-@dataclass
 class Foo:
     a: int = field(alias=["b", "c", "d"])
 
diff --git a/examples/any.py b/examples/any.py
index d2a2be2..6e467f4 100644
--- a/examples/any.py
+++ b/examples/any.py
@@ -1,4 +1,3 @@
-from dataclasses import dataclass
 from typing import Any
 
 from serde import serde
@@ -6,13 +5,11 @@ from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     v: float
 
 
 @serde
-@dataclass
 class Foo:
     a: Any
     b: Any
diff --git a/examples/class_var.py b/examples/class_var.py
index 617b404..9b08cd0 100644
--- a/examples/class_var.py
+++ b/examples/class_var.py
@@ -1,22 +1,19 @@
-from dataclasses import dataclass
-from typing import ClassVar, List
+from typing import ClassVar
 
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     v: int
 
 
 @serde(serialize_class_var=True)
-@dataclass
 class Foo:
     a: ClassVar[int] = 10
     b: ClassVar[Bar] = Bar(100)
-    c: ClassVar[List[Bar]] = [Bar(1), Bar(2)]
+    c: ClassVar[list[Bar]] = [Bar(1), Bar(2)]
 
 
 def main() -> None:
diff --git a/examples/collection.py b/examples/collection.py
index b760f45..87041b9 100644
--- a/examples/collection.py
+++ b/examples/collection.py
@@ -1,41 +1,20 @@
-import sys
-from dataclasses import dataclass
-from typing import Dict, List, Tuple
-
 from serde import serde
 from serde.json import from_json, to_json
 
-PY39 = sys.version_info[:3] >= (3, 9, 0)
-
 
 @serde
-@dataclass
 class Foo:
-    a: List[str]
-    b: Tuple[str, bool]
-    c: Dict[str, List[int]]
-
-
-# For python >= 3.9, you can use [PEP585](https://www.python.org/dev/peps/pep-0585/)
-# style type annotations for standard collections.
-if PY39:
-
-    @serde
-    @dataclass
-    class FooPy39:
-        a: list[str]
-        b: tuple[str, bool]
-        c: dict[str, list[int]]
+    a: list[str]
+    b: tuple[str, bool]
+    c: dict[str, list[int]]
 
 
 def main() -> None:
-    cls = Foo if not PY39 else FooPy39
-
-    h = cls(["1", "2"], ("foo", True), {"bar": [10, 20]})
+    h = Foo(["1", "2"], ("foo", True), {"bar": [10, 20]})
     print(f"Into Json: {to_json(h)}")
 
     s = '{"a": ["1", "2"], "b": ["foo", true], "c": {"bar": [10, 20]}}'
-    print(f"From Json: {from_json(cls, s)}")
+    print(f"From Json: {from_json(Foo, s)}")
 
 
 if __name__ == "__main__":
diff --git a/examples/custom_class_serializer.py b/examples/custom_class_serializer.py
index 150e923..80e081e 100644
--- a/examples/custom_class_serializer.py
+++ b/examples/custom_class_serializer.py
@@ -1,12 +1,11 @@
 from plum import dispatch
-from dataclasses import dataclass
 from datetime import datetime
 from serde import (
     serde,
     field,
 )
 from serde.json import from_json, to_json
-from typing import Type, Any, List
+from typing import Type, Any
 
 
 class MySerializer:
@@ -22,14 +21,13 @@ class MyDeserializer:
 
 
 @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
-@dataclass
 class Foo:
     a: datetime
     b: datetime = field(
         serializer=lambda x: x.strftime("%y.%m.%d"),
         deserializer=lambda x: datetime.strptime(x, "%y.%m.%d"),
     )
-    c: List[datetime] = field(default_factory=list)
+    c: list[datetime] = field(default_factory=list)
 
 
 def main() -> None:
diff --git a/examples/custom_field_serializer.py b/examples/custom_field_serializer.py
index 2f157d8..565db9a 100644
--- a/examples/custom_field_serializer.py
+++ b/examples/custom_field_serializer.py
@@ -1,4 +1,3 @@
-from dataclasses import dataclass
 from datetime import datetime
 
 from serde import field, serde
@@ -6,7 +5,6 @@ from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Foo:
     a: datetime
     b: datetime = field(
diff --git a/examples/custom_legacy_class_serializer.py b/examples/custom_legacy_class_serializer.py
index ad779ba..ceb2a7a 100644
--- a/examples/custom_legacy_class_serializer.py
+++ b/examples/custom_legacy_class_serializer.py
@@ -1,4 +1,3 @@
-from dataclasses import dataclass
 from datetime import datetime
 from typing import Type, Any
 
@@ -31,7 +30,6 @@ def deserializer(cls: Type[Any], o: Any) -> datetime:
 
 
 @serde(serializer=serializer, deserializer=deserializer)
-@dataclass
 class Foo:
     a: datetime
     # Override by field serializer/deserializer.
diff --git a/examples/default.py b/examples/default.py
index eebdf4e..9c916bc 100644
--- a/examples/default.py
+++ b/examples/default.py
@@ -1,7 +1,4 @@
-from dataclasses import field
-from typing import Dict
-
-from serde import serde
+from serde import serde, field
 from serde.json import from_json, to_json
 
 
@@ -11,7 +8,7 @@ class Foo:
     s: str = "foo"
     f: float = field(default=100.0)  # Use dataclass field.
     b: bool = field(default=True)
-    d: Dict[str, int] = field(default_factory=dict)
+    d: dict[str, int] = field(default_factory=dict)
 
 
 def main() -> None:
diff --git a/examples/default_dict.py b/examples/default_dict.py
index 0c78d18..7a11a1e 100644
--- a/examples/default_dict.py
+++ b/examples/default_dict.py
@@ -1,31 +1,28 @@
 from collections import defaultdict
-from dataclasses import dataclass
-from typing import DefaultDict, List, Optional
+from typing import Optional
 
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     v: Optional[int] = None
 
 
 @serde
-@dataclass
 class Foo:
-    a: DefaultDict[str, List[int]]
-    b: DefaultDict[str, int]
-    c: DefaultDict[str, Bar]
+    a: defaultdict[str, list[int]]
+    b: defaultdict[str, int]
+    c: defaultdict[str, Bar]
 
 
 def main() -> None:
-    a: DefaultDict[str, List[int]] = defaultdict(list)
+    a: defaultdict[str, list[int]] = defaultdict(list)
     a["a"].append(1)
-    b: DefaultDict[str, int] = defaultdict(int)
+    b: defaultdict[str, int] = defaultdict(int)
     b["b"]
-    c: DefaultDict[str, Bar] = defaultdict(Bar)
+    c: defaultdict[str, Bar] = defaultdict(Bar)
     c["c"].v = 10
 
     f = Foo(a, b, c)
diff --git a/examples/enum34.py b/examples/enum34.py
index eca8626..c950c0b 100644
--- a/examples/enum34.py
+++ b/examples/enum34.py
@@ -1,5 +1,4 @@
 import enum
-from dataclasses import dataclass
 
 import imported
 
@@ -25,7 +24,6 @@ class IE(enum.IntEnum):
 
 
 @serde
-@dataclass
 class Foo:
     v0: IE
     v1: IE = IE.V1  # Default enum value.
diff --git a/examples/env.py b/examples/env.py
index 7dfda80..d2bbb2d 100644
--- a/examples/env.py
+++ b/examples/env.py
@@ -1,14 +1,14 @@
-from dataclasses import dataclass
-
 from envclasses import envclass, load_env
+from pathlib import Path
 
 from serde import deserialize
 from serde.yaml import from_yaml
 
+basedir = Path(__file__).parent
+
 
 @deserialize
 @envclass
-@dataclass
 class App:
     addr: str
     port: int
@@ -17,7 +17,7 @@ class App:
 
 
 def main() -> None:
-    with open("app.yml") as f:
+    with open(basedir / "app.yml") as f:
         yml = f.read()
     cfg = from_yaml(App, yml)
     print(cfg)
diff --git a/examples/field_order.py b/examples/field_order.py
index 00ddaee..89a4582 100644
--- a/examples/field_order.py
+++ b/examples/field_order.py
@@ -1,9 +1,7 @@
-from dataclasses import dataclass
 from serde import serde, field
 
 
 @serde
-@dataclass
 class Foo:
     id: int = field(rename="ID")  # Field with attributes can be defined before other fields
     # thanks to dataclass_transform field_specifiers
diff --git a/examples/flatten.py b/examples/flatten.py
index 175cf2f..273dae7 100644
--- a/examples/flatten.py
+++ b/examples/flatten.py
@@ -1,18 +1,14 @@
-from dataclasses import dataclass
-
 from serde import field, serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     c: float
     d: bool
 
 
 @serde
-@dataclass
 class Foo:
     a: int
     b: str
diff --git a/examples/forward_reference.py b/examples/forward_reference.py
index d3aeec5..d61a35d 100644
--- a/examples/forward_reference.py
+++ b/examples/forward_reference.py
@@ -1,6 +1,6 @@
 from __future__ import annotations
-from dataclasses import dataclass
 
+from dataclasses import dataclass
 from serde import serde
 from serde.json import from_json, to_json
 
@@ -13,7 +13,6 @@ class Foo:
 
 
 @serde
-@dataclass
 class Bar:
     f: float
     b: bool
diff --git a/examples/frozen_set.py b/examples/frozen_set.py
index 562152e..db1a46e 100644
--- a/examples/frozen_set.py
+++ b/examples/frozen_set.py
@@ -1,14 +1,10 @@
-from dataclasses import dataclass
-from typing import FrozenSet
-
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Foo:
-    i: FrozenSet[int]
+    i: frozenset[int]
 
 
 def main() -> None:
diff --git a/examples/generics.py b/examples/generics.py
index 436d447..9432a34 100644
--- a/examples/generics.py
+++ b/examples/generics.py
@@ -1,4 +1,3 @@
-from dataclasses import dataclass
 from typing import Generic, TypeVar
 
 from serde import from_dict, serde, to_dict
@@ -7,19 +6,16 @@ T = TypeVar("T")
 
 
 @serde
-@dataclass
 class Bar:
     n: int
 
 
 @serde
-@dataclass
 class Foo(Generic[T]):
     inner: T
 
 
 @serde
-@dataclass
 class Baz(Generic[T]):
     foo: Foo[T]
 
diff --git a/examples/generics_nested.py b/examples/generics_nested.py
index 8e80a77..5b34854 100644
--- a/examples/generics_nested.py
+++ b/examples/generics_nested.py
@@ -1,17 +1,14 @@
-from dataclasses import dataclass
 from typing import Generic, TypeVar
 
 from serde import from_dict, serde, to_dict
 
 
 @serde
-@dataclass
 class EventData:
     pass
 
 
 @serde
-@dataclass
 class A(EventData):
     a: str
 
@@ -22,14 +19,12 @@ Data = TypeVar("Data", bound=EventData)
 
 
 @serde
-@dataclass
 class Payload(Generic[Data]):
     id: int
     data: Data
 
 
 @serde
-@dataclass
 class Event(Generic[Data]):
     name: str
     payload: Payload[Data]
diff --git a/examples/global_custom_class_serializer.py b/examples/global_custom_class_serializer.py
index f3027ab..728df2c 100644
--- a/examples/global_custom_class_serializer.py
+++ b/examples/global_custom_class_serializer.py
@@ -1,5 +1,4 @@
 from plum import dispatch
-from dataclasses import dataclass
 from datetime import datetime
 from serde import serde, add_serializer, add_deserializer
 from serde.json import from_json, to_json
@@ -49,7 +48,6 @@ add_deserializer(MyDeserializer2())
 
 
 @serde(class_serializer=MySerializer3(), class_deserializer=MyDeserializer3())
-@dataclass
 class Foo:
     a: datetime
     b: int
diff --git a/examples/init_var.py b/examples/init_var.py
index 61c453f..5dcdc76 100644
--- a/examples/init_var.py
+++ b/examples/init_var.py
@@ -1,4 +1,4 @@
-from dataclasses import InitVar, dataclass
+from dataclasses import InitVar
 from typing import Optional
 
 from serde import serde, field
@@ -6,13 +6,12 @@ from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Foo:
     a: int
     b: Optional[int] = field(default=None, init=False)
     c: InitVar[Optional[int]] = 1000
 
-    def __post_init__(self, c: Optional[int]) -> None:  # type: ignore
+    def __post_init__(self, c: Optional[int]) -> None:
         self.b = self.a * 10
 
 
diff --git a/examples/jsonfile.py b/examples/jsonfile.py
index cbbda41..74ca7f9 100644
--- a/examples/jsonfile.py
+++ b/examples/jsonfile.py
@@ -8,32 +8,28 @@ Usage:
     $ poetry run python jsonfile.py
 """
 
-from dataclasses import dataclass
-from typing import List, Optional
+from typing import Optional
 
 from serde import serde
 from serde.json import from_json
 
 
 @serde
-@dataclass
 class Slide:
     title: str
     type: str
-    items: Optional[List[str]]
+    items: Optional[list[str]]
 
 
 @serde
-@dataclass
 class Slideshow:
     author: str
     date: str
-    slides: List[Slide]
+    slides: list[Slide]
     title: str
 
 
 @serde
-@dataclass
 class Data:
     slideshow: Slideshow
 
diff --git a/examples/lazy_type_evaluation.py b/examples/lazy_type_evaluation.py
index 45f2b07..44f684d 100644
--- a/examples/lazy_type_evaluation.py
+++ b/examples/lazy_type_evaluation.py
@@ -1,21 +1,17 @@
 from __future__ import annotations
 
-from dataclasses import dataclass
-
 from serde import serde
 from serde.json import from_json, to_json
-from typing import Type
 
 
 @serde
-@dataclass
 class Foo:
     i: int
     s: str
     f: float
     b: bool
 
-    def foo(self, cls: Type[Foo]) -> None:  # You can use "Foo" type before it's defined.
+    def foo(self, cls: type[Foo]) -> None:  # You can use "Foo" type before it's defined.
         print("foo")
 
 
diff --git a/examples/literal.py b/examples/literal.py
index 019fa9c..b87f709 100644
--- a/examples/literal.py
+++ b/examples/literal.py
@@ -1,5 +1,3 @@
-from dataclasses import dataclass
-
 from serde.compat import SerdeError
 from typing import Literal
 
@@ -8,7 +6,6 @@ from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Foo:
     i: Literal[1, 2]
     s: Literal["a", "b"]
diff --git a/examples/msg_pack.py b/examples/msg_pack.py
index 2365613..6539836 100644
--- a/examples/msg_pack.py
+++ b/examples/msg_pack.py
@@ -1,11 +1,8 @@
-from dataclasses import dataclass
-
 from serde import serde
 from serde.msgpack import from_msgpack, to_msgpack
 
 
 @serde
-@dataclass
 class Foo:
     i: int
     s: str
diff --git a/examples/nested.py b/examples/nested.py
index 4f85593..f9ca8a4 100644
--- a/examples/nested.py
+++ b/examples/nested.py
@@ -1,17 +1,13 @@
-from dataclasses import dataclass
-
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     b: int
 
 
 @serde
-@dataclass
 class Foo:
     a: Bar
 
diff --git a/examples/newtype.py b/examples/newtype.py
index dfe2770..17fcf40 100644
--- a/examples/newtype.py
+++ b/examples/newtype.py
@@ -1,4 +1,3 @@
-from dataclasses import dataclass
 from typing import NewType
 
 from serde import serde
@@ -8,7 +7,6 @@ UserId = NewType("UserId", int)
 
 
 @serde
-@dataclass
 class Foo:
     id: UserId
 
diff --git a/examples/primitive_subclass.py b/examples/primitive_subclass.py
index 0832ac7..f5f4725 100644
--- a/examples/primitive_subclass.py
+++ b/examples/primitive_subclass.py
@@ -1,7 +1,5 @@
-from dataclasses import dataclass
 from serde.json import from_json, to_json
 from serde import field, serde
-from typing import Dict, List
 
 
 class Id(str):
@@ -10,11 +8,10 @@ class Id(str):
 
 
 @serde
-@dataclass
 class Foo:
     a: Id = field(default_factory=Id)
-    b: Dict[Id, float] = field(default_factory=dict)
-    c: List[Id] = field(default_factory=list)
+    b: dict[Id, float] = field(default_factory=dict)
+    c: list[Id] = field(default_factory=list)
 
 
 def main() -> None:
diff --git a/examples/python_pickle.py b/examples/python_pickle.py
index 2c7f8fe..a966948 100644
--- a/examples/python_pickle.py
+++ b/examples/python_pickle.py
@@ -1,11 +1,8 @@
-from dataclasses import dataclass
-
 from serde import serde
 from serde.pickle import from_pickle, to_pickle
 
 
 @serde
-@dataclass
 class Foo:
     i: int
     s: str
diff --git a/examples/recursive_list.py b/examples/recursive_list.py
index d994bf0..9e76baf 100644
--- a/examples/recursive_list.py
+++ b/examples/recursive_list.py
@@ -1,5 +1,5 @@
+from __future__ import annotations
 from dataclasses import dataclass
-from typing import List
 
 from serde import serde
 from serde.json import from_json, to_json
@@ -8,7 +8,7 @@ from serde.json import from_json, to_json
 @dataclass
 class Node:
     name: str
-    children: List["Node"]
+    children: list[Node]
 
 
 serde(Node)
diff --git a/examples/recursive_union.py b/examples/recursive_union.py
index 80f41cb..35660a6 100644
--- a/examples/recursive_union.py
+++ b/examples/recursive_union.py
@@ -1,4 +1,4 @@
-from typing import List, Union
+from typing import Union
 from serde import serde, to_dict, InternalTagging, from_dict
 from dataclasses import dataclass
 
@@ -12,7 +12,7 @@ class Leaf:
 @dataclass
 class Node:
     name: str
-    children: List[Union[Leaf, "Node"]]
+    children: list[Union[Leaf, "Node"]]
 
 
 serde(Node, tagging=InternalTagging("type"))
diff --git a/examples/rename.py b/examples/rename.py
index 99f5854..df63464 100644
--- a/examples/rename.py
+++ b/examples/rename.py
@@ -7,14 +7,11 @@ Usage:
     $ poetry run python rename.py
 """
 
-from dataclasses import dataclass
-
 from serde import field, serde
 from serde.json import to_json
 
 
 @serde
-@dataclass
 class Foo:
     # Use 'class_name' because 'class' is a keyword.
     class_name: str = field(rename="class")
diff --git a/examples/rename_all.py b/examples/rename_all.py
index 5415b6e..97b34b0 100644
--- a/examples/rename_all.py
+++ b/examples/rename_all.py
@@ -7,7 +7,6 @@ Usage:
     $ poetry run python rename_all.py
 """
 
-from dataclasses import dataclass
 from typing import Optional
 
 from serde import serde
@@ -15,7 +14,6 @@ from serde.json import from_json, to_json
 
 
 @serde(rename_all="pascalcase")
-@dataclass
 class Foo:
     name: str
     no: Optional[int] = None
diff --git a/examples/simple.py b/examples/simple.py
index 7c84d89..1531026 100644
--- a/examples/simple.py
+++ b/examples/simple.py
@@ -1,11 +1,8 @@
-from dataclasses import dataclass
-
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Foo:
     i: int
     s: str
diff --git a/examples/skip.py b/examples/skip.py
index 841c6d1..d077c29 100644
--- a/examples/skip.py
+++ b/examples/skip.py
@@ -8,26 +8,21 @@ Usage:
     $ poetry run python skip.py
 """
 
-from dataclasses import dataclass
-from typing import Dict, List
-
 from serde import field, serde
 from serde.json import to_json
 
 
 @serde
-@dataclass
 class Resource:
     name: str
     hash: str
-    metadata: Dict[str, str] = field(default_factory=dict, skip=True)
+    metadata: dict[str, str] = field(default_factory=dict, skip=True)
 
 
 @serde
-@dataclass
 class World:
     player: str
-    enemies: List[str] = field(default_factory=list, skip_if_false=True)
+    enemies: list[str] = field(default_factory=list, skip_if_false=True)
     buddy: str = field(default="", skip_if=lambda v: v == "Pikachu")
     town: str = field(default="Masara Town", skip_if_default=True)
 
diff --git a/examples/tomlfile.py b/examples/tomlfile.py
index 90c0391..9ed78a1 100644
--- a/examples/tomlfile.py
+++ b/examples/tomlfile.py
@@ -8,15 +8,16 @@ Usage:
     $ poetry run python tomlfile.py
 """
 
-from dataclasses import dataclass
-from typing import Dict, List, Optional, Union
+from typing import Optional, Union
+from pathlib import Path
 
 from serde import Untagged, serde
 from serde.toml import from_toml
 
+basedir = Path(__file__).parent
+
 
 @serde
-@dataclass
 class Source:
     url: str
     verify_ssl: bool
@@ -24,13 +25,11 @@ class Source:
 
 
 @serde
-@dataclass
 class Requires:
     python_version: str
 
 
 @serde
-@dataclass
 class Package:
     path: Optional[str] = None
     version: Optional[str] = None
@@ -38,15 +37,14 @@ class Package:
 
 
 @serde(tagging=Untagged)
-@dataclass
 class Pipfile:
-    source: List[Source]
+    source: list[Source]
     requires: Optional[Requires]
-    packages: Dict[str, Union[str, Package]]
+    packages: dict[str, Union[str, Package]]
 
 
 def main() -> None:
-    with open("Pipfile") as f:
+    with open(basedir / "Pipfile") as f:
         toml = f.read()
     pip = from_toml(Pipfile, toml)
     print(pip)
diff --git a/examples/type_check_coerce.py b/examples/type_check_coerce.py
index 5b80d67..3f46b63 100644
--- a/examples/type_check_coerce.py
+++ b/examples/type_check_coerce.py
@@ -1,22 +1,19 @@
-from dataclasses import dataclass
-from typing import Dict, List, Optional
+from typing import Optional
 
 from serde import coerce, serde
 from serde.json import from_json, to_json
 
 
 @serde(type_check=coerce)
-@dataclass
 class Bar:
     e: int
 
 
 @serde(type_check=coerce)
-@dataclass
 class Foo:
     a: int
-    b: List[int]
-    c: List[Dict[str, int]]
+    b: list[int]
+    c: list[dict[str, int]]
     d: Optional[Bar] = None
 
 
diff --git a/examples/type_check_disabled.py b/examples/type_check_disabled.py
index c6b30e1..641bd42 100644
--- a/examples/type_check_disabled.py
+++ b/examples/type_check_disabled.py
@@ -1,5 +1,3 @@
-from typing import Dict, List
-
 from serde import disabled, serde
 from serde.json import from_json, to_json
 
@@ -7,8 +5,8 @@ from serde.json import from_json, to_json
 @serde(type_check=disabled)
 class Foo:
     a: int
-    b: List[int]
-    c: List[Dict[str, int]]
+    b: list[int]
+    c: list[dict[str, int]]
 
 
 def main() -> None:
diff --git a/examples/type_datetime.py b/examples/type_datetime.py
index a9fedb6..30aeacc 100644
--- a/examples/type_datetime.py
+++ b/examples/type_datetime.py
@@ -1,12 +1,10 @@
 import datetime
-from dataclasses import dataclass
 
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Foo:
     d: datetime.date
     t: datetime.time
diff --git a/examples/type_decimal.py b/examples/type_decimal.py
index a2af3ea..e4fa836 100644
--- a/examples/type_decimal.py
+++ b/examples/type_decimal.py
@@ -1,4 +1,3 @@
-from dataclasses import dataclass
 from decimal import Decimal
 
 from serde import serde
@@ -6,7 +5,6 @@ from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Foo:
     v: Decimal
 
diff --git a/examples/union.py b/examples/union.py
index 7e44612..fab665c 100644
--- a/examples/union.py
+++ b/examples/union.py
@@ -1,27 +1,23 @@
-from dataclasses import dataclass
-from typing import Dict, List, Union
+from typing import Union
 
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     v: int
 
 
 @serde
-@dataclass
 class Baz:
     v: float
 
 
 @serde
-@dataclass
 class Foo:
     a: Union[int, str]
-    b: Union[Dict[str, int], List[int]]
+    b: Union[dict[str, int], list[int]]
     c: Union[Bar, Baz]
 
 
diff --git a/examples/union_directly.py b/examples/union_directly.py
index 2cca285..559ff14 100644
--- a/examples/union_directly.py
+++ b/examples/union_directly.py
@@ -1,4 +1,3 @@
-from dataclasses import dataclass
 from typing import Union
 
 from serde import serde, Untagged, AdjacentTagging, InternalTagging
@@ -6,13 +5,11 @@ from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     b: int
 
 
 @serde
-@dataclass(unsafe_hash=True)
 class Baz:
     b: int
 
diff --git a/examples/union_operator.py b/examples/union_operator.py
index 3f40fb6..08e37f9 100644
--- a/examples/union_operator.py
+++ b/examples/union_operator.py
@@ -1,24 +1,20 @@
 # mypy: ignore-errors
-from dataclasses import dataclass
 
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     v: int
 
 
 @serde
-@dataclass
 class Baz:
     v: float
 
 
 @serde
-@dataclass
 class Foo:
     a: int | str  # type: ignore
     b: dict[str, int] | list[int]  # type: ignore
diff --git a/examples/union_tagging.py b/examples/union_tagging.py
index 8385c0e..090172c 100644
--- a/examples/union_tagging.py
+++ b/examples/union_tagging.py
@@ -1,4 +1,3 @@
-from dataclasses import dataclass
 from typing import Union
 
 from serde import AdjacentTagging, InternalTagging, Untagged, serde
@@ -6,20 +5,17 @@ from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Bar:
     b: int
 
 
 @serde
-@dataclass
 class Baz:
     b: int
 
 
 def external_tagging() -> None:
     @serde
-    @dataclass
     class Foo:
         a: Union[Bar, Baz]
 
@@ -32,7 +28,6 @@ def external_tagging() -> None:
 
 def internal_tagging() -> None:
     @serde(tagging=InternalTagging("type"))
-    @dataclass
     class Foo:
         a: Union[Bar, Baz]
 
@@ -45,7 +40,6 @@ def internal_tagging() -> None:
 
 def adjacent_tagging() -> None:
     @serde(tagging=AdjacentTagging(tag="type", content="content"))
-    @dataclass
     class Foo:
         a: Union[Bar, Baz]
 
@@ -58,7 +52,6 @@ def adjacent_tagging() -> None:
 
 def untagged() -> None:
     @serde(tagging=Untagged)
-    @dataclass
     class Foo:
         a: Union[Bar, Baz]
 
diff --git a/examples/user_exception.py b/examples/user_exception.py
index 8eede0b..e184cc2 100644
--- a/examples/user_exception.py
+++ b/examples/user_exception.py
@@ -1,5 +1,3 @@
-from dataclasses import dataclass
-
 from serde import serde
 from serde.json import from_json
 
@@ -9,7 +7,6 @@ class MyException(Exception):
 
 
 @serde
-@dataclass
 class Foo:
     v: int
 
diff --git a/examples/variable_length_tuple.py b/examples/variable_length_tuple.py
index c3dfe56..6ab3128 100644
--- a/examples/variable_length_tuple.py
+++ b/examples/variable_length_tuple.py
@@ -1,14 +1,10 @@
-from dataclasses import dataclass
-from typing import Tuple
-
 from serde import serde
 from serde.json import from_json, to_json
 
 
 @serde
-@dataclass
 class Foo:
-    v: Tuple[int, ...]
+    v: tuple[int, ...]
 
 
 def main() -> None:
diff --git a/examples/yamlfile.py b/examples/yamlfile.py
index 488b72e..a732949 100644
--- a/examples/yamlfile.py
+++ b/examples/yamlfile.py
@@ -8,15 +8,16 @@ Usage:
     $ poetry run python yamlfile.py
 """
 
-from dataclasses import dataclass
-from typing import Dict, List, Optional, Union
+from typing import Optional, Union
+import pathlib
 
 from serde import Untagged, serde
 from serde.yaml import from_yaml
 
+basedir = pathlib.Path(__file__).parent
+
 
 @serde(rename_all="camelcase")
-@dataclass
 class Info:
     title: str
     description: str
@@ -24,7 +25,6 @@ class Info:
 
 
 @serde(rename_all="camelcase")
-@dataclass
 class Parameter:
     name: str
     # not yet supported.
@@ -34,49 +34,44 @@ class Parameter:
 
 
 @serde(rename_all="camelcase")
-@dataclass
 class Response:
     description: str
 
 
 @serde(rename_all="camelcase", tagging=Untagged)
-@dataclass
 class Path:
     description: str
     operation_id: str
-    parameters: List[Union[str, Parameter]]
-    responses: Dict[Union[str, int], Response]
+    parameters: list[Union[str, Parameter]]
+    responses: dict[Union[str, int], Response]
 
 
 @serde(rename_all="camelcase")
-@dataclass
 class Prop:
     type: str
     format: Optional[str]
 
 
 @serde(rename_all="camelcase")
-@dataclass
 class Definition:
-    required: Optional[List[str]]
-    properties: Dict[str, Prop]
+    required: Optional[list[str]]
+    properties: dict[str, Prop]
 
 
 @serde(rename_all="camelcase")
-@dataclass
 class Swagger:
     swagger: int
     info: Info
     host: str
-    schemes: List[str]
+    schemes: list[str]
     base_path = str
-    produces: List[str]
-    paths: Dict[str, Dict[str, Path]]
-    definitions: Dict[str, Definition]
+    produces: list[str]
+    paths: dict[str, dict[str, Path]]
+    definitions: dict[str, Definition]
 
 
 def main() -> None:
-    with open("swagger.yml") as f:
+    with open(basedir / "swagger.yml") as f:
         yaml = f.read()
     swagger = from_yaml(Swagger, yaml)
     print(swagger)
diff --git a/pyproject.toml b/pyproject.toml
index 4e81d4b..76a4008 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -14,7 +14,6 @@ classifiers=[
         "Development Status :: 4 - Beta",
         "Intended Audience :: Developers",
         "License :: OSI Approved :: MIT License",
-        "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
@@ -24,7 +23,7 @@ classifiers=[
     ]
 
 [tool.poetry.dependencies]
-python = "^3.8.0"
+python = "^3.9.0"
 typing_inspect = ">=0.4.0"
 typing_extensions = { version = ">=4.1.0" }
 casefy = "*"
@@ -34,7 +33,6 @@ tomli = { version = "*", markers = "extra == 'toml' or extra == 'all'", optional
 tomli-w = { version = "*", markers = "extra == 'toml' or extra == 'all'", optional = true }
 pyyaml = { version = "*", markers = "extra == 'yaml' or extra == 'all'", optional = true }
 numpy = [
-    { version = ">1.21.0", markers = "python_version ~= '3.8.0' and (extra == 'numpy' or extra == 'all')", optional = true },
     { version = ">1.21.0", markers = "python_version ~= '3.9.0' and (extra == 'numpy' or extra == 'all')", optional = true },
     { version = ">1.22.0", markers = "python_version ~= '3.10' and (extra == 'numpy' or extra == 'all')", optional = true },
     { version = ">1.22.0", markers = "python_version ~= '3.11' and (extra == 'numpy' or extra == 'all')", optional = true },
@@ -50,9 +48,10 @@ tomli = { version = "*", markers = "python_version <= '3.11.0'" }
 tomli-w = "*"
 msgpack = "*"
 numpy = [
-    { version = ">1.21.0", markers = "python_version ~= '3.8.0'" },
     { version = ">1.21.0", markers = "python_version ~= '3.9.0'" },
     { version = ">1.22.0", markers = "python_version ~= '3.10'" },
+    { version = ">1.22.0", markers = "python_version ~= '3.11'" },
+    { version = ">1.22.0", markers = "python_version ~= '3.12'" },
 ]
 mypy = "==1.9.0"
 pytest = "*"
diff --git a/serde/__init__.py b/serde/__init__.py
index 435ceb1..e62e75a 100644
--- a/serde/__init__.py
+++ b/serde/__init__.py
@@ -22,7 +22,8 @@ Other modules
 """
 
 from dataclasses import dataclass
-from typing import Callable, Optional, Type, overload, Any
+from collections.abc import Callable
+from typing import Optional, overload, Any
 
 from typing_extensions import dataclass_transform
 
@@ -112,7 +113,7 @@ __all__ = [
 
 @overload
 def serde(
-    _cls: Type[T],
+    _cls: type[T],
     rename_all: Optional[str] = None,
     reuse_instances_default: bool = True,
     convert_sets_default: bool = False,
@@ -123,7 +124,7 @@ def serde(
     serialize_class_var: bool = False,
     class_serializer: Optional[ClassSerializer] = None,
     class_deserializer: Optional[ClassDeserializer] = None,
-) -> Type[T]: ...
+) -> type[T]: ...
 
 
 @overload
@@ -138,7 +139,7 @@ def serde(
     serialize_class_var: bool = False,
     class_serializer: Optional[ClassSerializer] = None,
     class_deserializer: Optional[ClassDeserializer] = None,
-) -> Callable[[Type[T]], Type[T]]: ...
+) -> Callable[[type[T]], type[T]]: ...
 
 
 @dataclass_transform(field_specifiers=(field,))  # type: ignore
diff --git a/serde/compat.py b/serde/compat.py
index 50e3b71..be2e159 100644
--- a/serde/compat.py
+++ b/serde/compat.py
@@ -13,78 +13,25 @@ import pathlib
 import sys
 import types
 import uuid
+import typing
 from collections import defaultdict
+from collections.abc import Iterator
 from dataclasses import is_dataclass
-import typing
-from typing import TypeVar, Generic, Any, ClassVar, Iterator, Optional, NewType
-from beartype.typing import (
-    DefaultDict,
-    Dict,
-    FrozenSet,
-    List,
-    Set,
-    Tuple,
-    Union,
-)
+from typing import TypeVar, Generic, Any, ClassVar, Optional, NewType, Union
 
-import typing_extensions
 import typing_inspect
-from typing_extensions import Type, TypeGuard, TypeAlias
+from typing_extensions import TypeGuard
 
-# Create alias for `dataclasses.Field` because `dataclasses.Field` is a generic
-# class since 3.9 but is not in 3.7 and 3.8.
-DataclassField: TypeAlias = (
-    dataclasses.Field if sys.version_info[:2] <= (3, 8) else dataclasses.Field[Any]  # type: ignore
-)
 
-try:
-    if sys.version_info[:2] <= (3, 8):
-        import numpy as np
-
-        def __is_nptype(tp):
-            origin = getattr(tp, "__origin__", None)
-            ndarray = getattr(np, "ndarray", None)
-            dtype = getattr(np, "dtype", None)
-            if not origin or not ndarray or not dtype:
-                return False
-            return origin in (ndarray, dtype)
-
-        # If the given type is NDArray or _DType, returns __origin__ or __args__.
-        # This should work since the only GenericAliases that current NumPy (1.23)
-        # exposes are 'NDArray' and '_DType'.
-        # Note: these functions are only needed on Python 3.8 or earlier.
-        # On Python >= 3.9, numpy.ndarray[...] and numpy.dtype[...] are instances of
-        # the builtin genericalias class.
-        def get_np_origin(tp: Type[Any]) -> Optional[Any]:
-            if __is_nptype(tp):
-                return getattr(tp, "__origin__", None)
-            else:
-                return None
+def get_np_origin(tp: type[Any]) -> Optional[Any]:
+    return None
 
-        def get_np_args(tp: Any) -> Tuple[Any, ...]:
-            if __is_nptype(tp):
-                return tp.__args__
-            else:
-                return ()
 
-    else:
-
-        def get_np_origin(tp: Type[Any]) -> Optional[Any]:
-            return None
+def get_np_args(tp: Any) -> tuple[Any, ...]:
+    return ()
 
-        def get_np_args(tp: Any) -> Tuple[Any, ...]:
-            return ()
 
-except ImportError:
-
-    def get_np_origin(tp: Type[Any]) -> Optional[Any]:
-        return None
-
-    def get_np_args(tp: Any) -> Tuple[Any, ...]:
-        return ()
-
-
-__all__: List[str] = []
+__all__: list[str] = []
 
 T = TypeVar("T")
 
@@ -148,44 +95,36 @@ def get_origin(typ: Any) -> Optional[Any]:
     """
     Provide `get_origin` that works in all python versions.
     """
-    try:
-        return typing.get_origin(typ) or get_np_origin(
-            typ
-        )  # python>=3.8 typing module has get_origin.
-    except AttributeError:
-        return typing_extensions.get_origin(typ) or get_np_origin(typ)
+    return typing.get_origin(typ) or get_np_origin(typ)
 
 
-def get_args(typ: Any) -> Tuple[Any, ...]:
+def get_args(typ: Any) -> tuple[Any, ...]:
     """
     Provide `get_args` that works in all python versions.
     """
-    try:
-        return typing.get_args(typ) or get_np_args(typ)  # python>=3.8 typing module has get_args.
-    except AttributeError:
-        return typing_extensions.get_args(typ) or get_np_args(typ)
+    return typing.get_args(typ) or get_np_args(typ)
 
 
 def typename(typ: Any, with_typing_module: bool = False) -> str:
     """
-    >>> from typing import List, Dict, Set, Any
+    >>> from typing import Any
     >>> typename(int)
     'int'
     >>> class Foo: pass
     >>> typename(Foo)
     'Foo'
-    >>> typename(List[Foo])
-    'List[Foo]'
-    >>> typename(Dict[str, Foo])
-    'Dict[str, Foo]'
-    >>> typename(Tuple[int, str, Foo, List[int], Dict[str, Foo]])
-    'Tuple[int, str, Foo, List[int], Dict[str, Foo]]'
-    >>> typename(Optional[List[Foo]])
-    'Optional[List[Foo]]'
-    >>> typename(Union[Optional[Foo], List[Foo], Union[str, int]])
-    'Union[Optional[Foo], List[Foo], str, int]'
-    >>> typename(Set[Foo])
-    'Set[Foo]'
+    >>> typename(list[Foo])
+    'list[Foo]'
+    >>> typename(dict[str, Foo])
+    'dict[str, Foo]'
+    >>> typename(tuple[int, str, Foo, list[int], dict[str, Foo]])
+    'tuple[int, str, Foo, list[int], dict[str, Foo]]'
+    >>> typename(Optional[list[Foo]])
+    'Optional[list[Foo]]'
+    >>> typename(Union[Optional[Foo], list[Foo], Union[str, int]])
+    'Union[Optional[Foo], list[Foo], str, int]'
+    >>> typename(set[Foo])
+    'set[Foo]'
     >>> typename(Any)
     'Any'
     """
@@ -204,48 +143,33 @@ def typename(typ: Any, with_typing_module: bool = False) -> str:
         else:
             return f"{mod}Union"
     elif is_list(typ):
-        # Workaround for python 3.7.
-        # get_args for the bare List returns parameter T.
-        if typ is List:
-            return f"{mod}List"
-
         args = type_args(typ)
         if args:
             et = thisfunc(args[0])
-            return f"{mod}List[{et}]"
+            return f"{mod}list[{et}]"
         else:
-            return f"{mod}List"
+            return f"{mod}list"
     elif is_set(typ):
-        # Workaround for python 3.7.
-        # get_args for the bare Set returns parameter T.
-        if typ is Set:
-            return f"{mod}Set"
-
         args = type_args(typ)
         if args:
             et = thisfunc(args[0])
-            return f"{mod}Set[{et}]"
+            return f"{mod}set[{et}]"
         else:
-            return f"{mod}Set"
+            return f"{mod}set"
     elif is_dict(typ):
-        # Workaround for python 3.7.
-        # get_args for the bare Dict returns parameter K, V.
-        if typ is Dict:
-            return f"{mod}Dict"
-
         args = type_args(typ)
         if args and len(args) == 2:
             kt = thisfunc(args[0])
             vt = thisfunc(args[1])
-            return f"{mod}Dict[{kt}, {vt}]"
+            return f"{mod}dict[{kt}, {vt}]"
         else:
-            return f"{mod}Dict"
+            return f"{mod}dict"
     elif is_tuple(typ):
         args = type_args(typ)
         if args:
-            return f'{mod}Tuple[{", ".join([thisfunc(e) for e in args])}]'
+            return f'{mod}tuple[{", ".join([thisfunc(e) for e in args])}]'
         else:
-            return f"{mod}Tuple"
+            return f"{mod}tuple"
     elif is_generic(typ):
         origin = get_origin(typ)
         if origin is None:
@@ -282,12 +206,12 @@ def typename(typ: Any, with_typing_module: bool = False) -> str:
                 raise SerdeError(f"Could not get a type name from: {typ}")
 
 
-def type_args(typ: Any) -> Tuple[Type[Any], ...]:
+def type_args(typ: Any) -> tuple[type[Any], ...]:
     """
     Wrapper to suppress type error for accessing private members.
     """
     try:
-        args: Tuple[Type[Any, ...]] = typ.__args__  # type: ignore
+        args: tuple[type[Any, ...]] = typ.__args__  # type: ignore
         if args is None:
             return ()
         else:
@@ -296,7 +220,7 @@ def type_args(typ: Any) -> Tuple[Type[Any], ...]:
         return get_args(typ)
 
 
-def union_args(typ: Any) -> Tuple[Type[Any], ...]:
+def union_args(typ: Any) -> tuple[type[Any], ...]:
     if not is_union(typ):
         raise TypeError(f"{typ} is not Union")
     args = type_args(typ)
@@ -314,7 +238,7 @@ def union_args(typ: Any) -> Tuple[Type[Any], ...]:
     return tuple(types)
 
 
-def dataclass_fields(cls: Type[Any]) -> Iterator[dataclasses.Field]:  # type: ignore
+def dataclass_fields(cls: type[Any]) -> Iterator[dataclasses.Field]:  # type: ignore
     raw_fields = dataclasses.fields(cls)
 
     try:
@@ -337,17 +261,17 @@ def dataclass_fields(cls: Type[Any]) -> Iterator[dataclasses.Field]:  # type: ig
     return iter(raw_fields)
 
 
-TypeLike = Union[Type[Any], typing.Any]
+TypeLike = Union[type[Any], typing.Any]
 
 
-def iter_types(cls: TypeLike) -> List[TypeLike]:
+def iter_types(cls: TypeLike) -> list[TypeLike]:
     """
     Iterate field types recursively.
 
     The correct return type is `Iterator[Union[Type, typing._specialform]],
     but `typing._specialform` doesn't exist for python 3.6. Use `Any` instead.
     """
-    lst: Set[TypeLike] = set()
+    lst: set[TypeLike] = set()
 
     def recursive(cls: TypeLike) -> None:
         if cls in lst:
@@ -369,21 +293,21 @@ def iter_types(cls: TypeLike) -> List[TypeLike]:
             for arg in type_args(cls):
                 recursive(arg)
         elif is_list(cls):
-            lst.add(List)
+            lst.add(list)
             args = type_args(cls)
             if args:
                 recursive(args[0])
         elif is_set(cls):
-            lst.add(Set)
+            lst.add(set)
             args = type_args(cls)
             if args:
                 recursive(args[0])
         elif is_tuple(cls):
-            lst.add(Tuple)
+            lst.add(tuple)
             for arg in type_args(cls):
                 recursive(arg)
         elif is_dict(cls):
-            lst.add(Dict)
+            lst.add(dict)
             args = type_args(cls)
             if args and len(args) >= 2:
                 recursive(args[0])
@@ -395,12 +319,12 @@ def iter_types(cls: TypeLike) -> List[TypeLike]:
     return list(lst)
 
 
-def iter_unions(cls: TypeLike) -> List[TypeLike]:
+def iter_unions(cls: TypeLike) -> list[TypeLike]:
     """
     Iterate over all unions that are used in the dataclass
     """
-    lst: Set[TypeLike] = set()
-    stack: List[TypeLike] = []  # To prevent infinite recursion
+    lst: set[TypeLike] = set()
+    stack: list[TypeLike] = []  # To prevent infinite recursion
 
     def recursive(cls: TypeLike) -> None:
         if cls in lst:
@@ -438,11 +362,11 @@ def iter_unions(cls: TypeLike) -> List[TypeLike]:
     return list(lst)
 
 
-def iter_literals(cls: TypeLike) -> List[TypeLike]:
+def iter_literals(cls: TypeLike) -> list[TypeLike]:
     """
     Iterate over all literals that are used in the dataclass
     """
-    lst: Set[TypeLike] = set()
+    lst: set[TypeLike] = set()
 
     def recursive(cls: TypeLike) -> None:
         if cls in lst:
@@ -554,68 +478,64 @@ def is_bare_opt(typ: Any) -> bool:
     return not type_args(typ) and typ is Optional
 
 
-def is_list(typ: Type[Any]) -> bool:
+def is_list(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.List`.
+    Test if the type is `list`.
 
-    >>> from typing import List
-    >>> is_list(List[int])
+    >>> is_list(list[int])
     True
-    >>> is_list(List)
+    >>> is_list(list)
     True
     """
     try:
         return issubclass(get_origin(typ), list)  # type: ignore
     except TypeError:
-        return typ in (List, list)
+        return typ is list
 
 
-def is_bare_list(typ: Type[Any]) -> bool:
+def is_bare_list(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.List` without type args.
+    Test if the type is `list` without type args.
 
-    >>> from typing import List
-    >>> is_bare_list(List[int])
+    >>> is_bare_list(list[int])
     False
-    >>> is_bare_list(List)
+    >>> is_bare_list(list)
     True
     """
-    return typ in (typing.List, List, list)
+    return typ is list
 
 
 def is_tuple(typ: Any) -> bool:
     """
-    Test if the type is `typing.Tuple`.
+    Test if the type is tuple.
     """
     try:
         return issubclass(get_origin(typ), tuple)  # type: ignore
     except TypeError:
-        return typ in (Tuple, tuple)
+        return typ is tuple
 
 
-def is_bare_tuple(typ: Type[Any]) -> bool:
+def is_bare_tuple(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.Tuple` without type args.
+    Test if the type is tuple without type args.
 
-    >>> from typing import Tuple
-    >>> is_bare_tuple(Tuple[int, str])
+    >>> is_bare_tuple(tuple[int, str])
     False
-    >>> is_bare_tuple(Tuple)
+    >>> is_bare_tuple(tuple)
     True
     """
-    return typ in (typing.Tuple, Tuple, tuple)
+    return typ is tuple
 
 
-def is_variable_tuple(typ: Type[Any]) -> bool:
+def is_variable_tuple(typ: type[Any]) -> bool:
     """
-    Test if the type is a variable length of tuple `typing.Tuple[T, ...]`.
+    Test if the type is a variable length of tuple tuple[T, ...]`.
 
-    >>> from typing import Tuple
-    >>> is_variable_tuple(Tuple[int, ...])
+    >>> is_variable_tuple(tuple[int, ...])
     True
-    >>> is_variable_tuple(Tuple[int, bool])
+    >>> is_variable_tuple(tuple[int, bool])
     False
-    >>> is_variable_tuple(Tuple[()])
+    >>> is_variable_tuple(tuple[()])
     False
     """
     istuple = is_tuple(typ) and not is_bare_tuple(typ)
@@ -623,101 +543,95 @@ def is_variable_tuple(typ: Type[Any]) -> bool:
     return istuple and len(args) == 2 and is_ellipsis(args[1])
 
 
-def is_set(typ: Type[Any]) -> bool:
+def is_set(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.Set` or `typing.FrozenSet`.
+    Test if the type is `set` or `frozenset`.
 
-    >>> from typing import Set
-    >>> is_set(Set[int])
+    >>> is_set(set[int])
     True
-    >>> is_set(Set)
+    >>> is_set(set)
     True
-    >>> is_set(FrozenSet[int])
+    >>> is_set(frozenset[int])
     True
     """
     try:
         return issubclass(get_origin(typ), (set, frozenset))  # type: ignore
     except TypeError:
-        return typ in (Set, set, FrozenSet, frozenset)
+        return typ in (set, frozenset)
 
 
-def is_bare_set(typ: Type[Any]) -> bool:
+def is_bare_set(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.Set` without type args.
+    Test if the type is `set` without type args.
 
-    >>> from typing import Set
-    >>> is_bare_set(Set[int])
+    >>> is_bare_set(set[int])
     False
-    >>> is_bare_set(Set)
+    >>> is_bare_set(set)
     True
     """
-    return typ in (typing.Set, Set, set)
+    return typ in (set, frozenset)
 
 
-def is_frozen_set(typ: Type[Any]) -> bool:
+def is_frozen_set(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.FrozenSet`.
+    Test if the type is `frozenset`.
 
-    >>> from typing import Set
-    >>> is_frozen_set(FrozenSet[int])
+    >>> is_frozen_set(frozenset[int])
     True
-    >>> is_frozen_set(Set)
+    >>> is_frozen_set(set)
     False
     """
     try:
         return issubclass(get_origin(typ), frozenset)  # type: ignore
     except TypeError:
-        return typ in (FrozenSet, frozenset)
+        return typ is frozenset
 
 
-def is_dict(typ: Type[Any]) -> bool:
+def is_dict(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.Dict`.
+    Test if the type is dict.
 
-    >>> from typing import Dict
-    >>> is_dict(Dict[int, int])
+    >>> is_dict(dict[int, int])
     True
-    >>> is_dict(Dict)
+    >>> is_dict(dict)
     True
-    >>> is_dict(DefaultDict[int, int])
+    >>> is_dict(defaultdict[int, int])
     True
     """
     try:
         return issubclass(get_origin(typ), (dict, defaultdict))  # type: ignore
     except TypeError:
-        return typ in (Dict, dict, DefaultDict, defaultdict)
+        return typ in (dict, defaultdict)
 
 
-def is_bare_dict(typ: Type[Any]) -> bool:
+def is_bare_dict(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.Dict` without type args.
+    Test if the type is `dict` without type args.
 
-    >>> from typing import Dict
-    >>> is_bare_dict(Dict[int, str])
+    >>> is_bare_dict(dict[int, str])
     False
-    >>> is_bare_dict(Dict)
+    >>> is_bare_dict(dict)
     True
     """
-    return typ in (typing.Dict, Dict, dict)
+    return typ is dict
 
 
-def is_default_dict(typ: Type[Any]) -> bool:
+def is_default_dict(typ: type[Any]) -> bool:
     """
-    Test if the type is `typing.DefaultDict`.
+    Test if the type is `defaultdict`.
 
-    >>> from typing import Dict
-    >>> is_default_dict(DefaultDict[int, int])
+    >>> is_default_dict(defaultdict[int, int])
     True
-    >>> is_default_dict(Dict[int, int])
+    >>> is_default_dict(dict[int, int])
     False
     """
     try:
         return issubclass(get_origin(typ), defaultdict)  # type: ignore
     except TypeError:
-        return typ in (DefaultDict, defaultdict)
+        return typ is defaultdict
 
 
-def is_none(typ: Type[Any]) -> bool:
+def is_none(typ: type[Any]) -> bool:
     """
     >>> is_none(int)
     False
@@ -732,7 +646,7 @@ def is_none(typ: Type[Any]) -> bool:
 PRIMITIVES = [int, float, bool, str]
 
 
-def is_enum(typ: Type[Any]) -> TypeGuard[enum.Enum]:
+def is_enum(typ: type[Any]) -> TypeGuard[enum.Enum]:
     """
     Test if the type is `enum.Enum`.
     """
@@ -742,7 +656,7 @@ def is_enum(typ: Type[Any]) -> TypeGuard[enum.Enum]:
         return isinstance(typ, enum.Enum)
 
 
-def is_primitive_subclass(typ: Type[Any]) -> bool:
+def is_primitive_subclass(typ: type[Any]) -> bool:
     """
     Test if the type is a subclass of primitive type.
 
@@ -756,7 +670,7 @@ def is_primitive_subclass(typ: Type[Any]) -> bool:
     return is_primitive(typ) and typ not in PRIMITIVES and not is_new_type_primitive(typ)
 
 
-def is_primitive(typ: Union[Type[Any], NewType]) -> bool:
+def is_primitive(typ: Union[type[Any], NewType]) -> bool:
     """
     Test if the type is primitive.
 
@@ -773,7 +687,7 @@ def is_primitive(typ: Union[Type[Any], NewType]) -> bool:
         return is_new_type_primitive(typ)
 
 
-def is_new_type_primitive(typ: Union[Type[Any], NewType]) -> bool:
+def is_new_type_primitive(typ: Union[type[Any], NewType]) -> bool:
     """
     Test if the type is a NewType of primitives.
     """
@@ -800,7 +714,7 @@ def is_generic(typ: Any) -> bool:
     return origin is not None and Generic in getattr(origin, "__bases__", ())
 
 
-def is_class_var(typ: Type[Any]) -> bool:
+def is_class_var(typ: type[Any]) -> bool:
     """
     Test if the type is `typing.ClassVar`.
 
@@ -812,7 +726,7 @@ def is_class_var(typ: Type[Any]) -> bool:
     return get_origin(typ) is ClassVar or typ is ClassVar  # type: ignore
 
 
-def is_literal(typ: Type[Any]) -> bool:
+def is_literal(typ: type[Any]) -> bool:
     """
     Test if the type is derived from `typing.Literal`.
 
@@ -828,14 +742,14 @@ def is_literal(typ: Type[Any]) -> bool:
     return origin is not None and origin is typing.Literal
 
 
-def is_any(typ: Type[Any]) -> bool:
+def is_any(typ: type[Any]) -> bool:
     """
     Test if the type is `typing.Any`.
     """
     return typ is Any
 
 
-def is_str_serializable(typ: Type[Any]) -> bool:
+def is_str_serializable(typ: type[Any]) -> bool:
     """
     Test if the type is serializable to `str`.
     """
@@ -843,7 +757,7 @@ def is_str_serializable(typ: Type[Any]) -> bool:
 
 
 def is_datetime(
-    typ: Type[Any],
+    typ: type[Any],
 ) -> TypeGuard[Union[datetime.date, datetime.time, datetime.datetime]]:
     """
     Test if the type is any of the datetime types..
@@ -863,7 +777,7 @@ def is_ellipsis(typ: Any) -> bool:
     return typ is Ellipsis
 
 
-def get_type_var_names(cls: Type[Any]) -> Optional[List[str]]:
+def get_type_var_names(cls: type[Any]) -> Optional[list[str]]:
     """
     Get type argument names of a generic class.
 
@@ -878,14 +792,14 @@ def get_type_var_names(cls: Type[Any]) -> Optional[List[str]]:
     if not bases:
         return None
 
-    type_arg_names: List[str] = []
+    type_arg_names: list[str] = []
     for base in bases:
         type_arg_names.extend(arg.__name__ for arg in get_args(base))
 
     return type_arg_names
 
 
-def find_generic_arg(cls: Type[Any], field: TypeVar) -> int:
+def find_generic_arg(cls: type[Any], field: TypeVar) -> int:
     """
     Find a type in generic parameters.
 
@@ -918,8 +832,8 @@ def find_generic_arg(cls: Type[Any], field: TypeVar) -> int:
 
 def get_generic_arg(
     typ: Any,
-    maybe_generic_type_vars: Optional[List[str]],
-    variable_type_args: Optional[List[str]],
+    maybe_generic_type_vars: Optional[list[str]],
+    variable_type_args: Optional[list[str]],
     index: int,
 ) -> Any:
     """
diff --git a/serde/core.py b/serde/core.py
index 65be1f0..883e688 100644
--- a/serde/core.py
+++ b/serde/core.py
@@ -9,31 +9,21 @@ import functools
 import logging
 import sys
 import re
+import casefy
 from dataclasses import dataclass
+from collections.abc import Mapping, Sequence, Callable
 from typing import (
     overload,
-    Dict,
-    Type,
     TypeVar,
     Generic,
     Optional,
     Any,
     Protocol,
-    Mapping,
-    Sequence,
-)
-from beartype.typing import (
-    Callable,
-    List,
+    get_type_hints,
     Union,
-    Tuple,
 )
 
-import casefy
-from typing_extensions import get_type_hints
-
 from .compat import (
-    DataclassField,
     T,
     SerdeError,
     dataclass_fields,
@@ -122,9 +112,9 @@ class Cache:
     should be only once.
     """
 
-    classes: Dict[str, Type[Any]] = dataclasses.field(default_factory=dict)
+    classes: dict[str, type[Any]] = dataclasses.field(default_factory=dict)
 
-    def _get_class(self, cls: Type[Any]) -> Type[Any]:
+    def _get_class(self, cls: type[Any]) -> type[Any]:
         """
         Get a wrapper class from the the cache. If not found, it will generate
         the class and store it in the cache.
@@ -133,7 +123,7 @@ class Cache:
         wrapper = self.classes.get(class_name)
         return wrapper or self._generate_class(cls)
 
-    def _generate_class(self, cls: Type[Any]) -> Type[Any]:
+    def _generate_class(self, cls: type[Any]) -> type[Any]:
         """
         Generate a wrapper dataclass then make the it (de)serializable using
         @serde decorator.
@@ -151,7 +141,7 @@ class Cache:
         logger.debug(f"(de)serializing code for {class_name} was generated")
         return wrapper
 
-    def serialize(self, cls: Type[Any], obj: Any, **kwargs: Any) -> Any:
+    def serialize(self, cls: type[Any], obj: Any, **kwargs: Any) -> Any:
         """
         Serialize the specified type of object into dict or tuple.
         """
@@ -163,7 +153,7 @@ class Cache:
 
         return data["v"]
 
-    def deserialize(self, cls: Type[T], obj: Any) -> T:
+    def deserialize(self, cls: type[T], obj: Any) -> T:
         """
         Deserialize from dict or tuple into the specified type.
         """
@@ -171,7 +161,7 @@ class Cache:
         scope: Scope = getattr(wrapper, SERDE_SCOPE)
         return scope.funcs[FROM_DICT](data={"v": obj}).v  # type: ignore
 
-    def _get_union_class(self, cls: Type[Any]) -> Optional[Type[Any]]:
+    def _get_union_class(self, cls: type[Any]) -> Optional[type[Any]]:
         """
         Get a wrapper class from the the cache. If not found, it will generate
         the class and store it in the cache.
@@ -183,7 +173,7 @@ class Cache:
         wrapper = self.classes.get(class_name)
         return wrapper or self._generate_union_class(cls)
 
-    def _generate_union_class(self, cls: Type[Any]) -> Type[Any]:
+    def _generate_union_class(self, cls: type[Any]) -> type[Any]:
         """
         Generate a wrapper dataclass then make the it (de)serializable using
         @serde decorator.
@@ -199,7 +189,7 @@ class Cache:
         self.classes[class_name] = wrapper
         return wrapper
 
-    def serialize_union(self, cls: Type[Any], obj: Any) -> Any:
+    def serialize_union(self, cls: type[Any], obj: Any) -> Any:
         """
         Serialize the specified Union into dict or tuple.
         """
@@ -209,7 +199,7 @@ class Cache:
         func_name = union_func_name(UNION_SE_PREFIX, list(type_args(union_cls)))
         return scope.funcs[func_name](obj, False, False)
 
-    def deserialize_union(self, cls: Type[T], data: Any) -> T:
+    def deserialize_union(self, cls: type[T], data: Any) -> T:
         """
         Deserialize from dict or tuple into the specified Union.
         """
@@ -220,7 +210,7 @@ class Cache:
         return scope.funcs[func_name](cls=union_cls, data=data)  # type: ignore
 
 
-def _extract_from_with_tagging(maybe_with_tagging: Any) -> Tuple[Any, Tagging]:
+def _extract_from_with_tagging(maybe_with_tagging: Any) -> tuple[Any, Tagging]:
     try:
         if isinstance(maybe_with_tagging, _WithTagging):
             union_cls = maybe_with_tagging.inner
@@ -244,20 +234,20 @@ class Scope:
     Container to store types and functions used in code generation context.
     """
 
-    cls: Type[Any]
+    cls: type[Any]
     """ The exact class this scope is for
     (needed to distinguish scopes between inherited classes) """
 
-    funcs: Dict[str, Callable[..., Any]] = dataclasses.field(default_factory=dict)
+    funcs: dict[str, Callable[..., Any]] = dataclasses.field(default_factory=dict)
     """ Generated serialize and deserialize functions """
 
-    defaults: Dict[str, Union[Callable[..., Any], Any]] = dataclasses.field(default_factory=dict)
+    defaults: dict[str, Union[Callable[..., Any], Any]] = dataclasses.field(default_factory=dict)
     """ Default values of the dataclass fields (factories & normal values) """
 
-    code: Dict[str, str] = dataclasses.field(default_factory=dict)
+    code: dict[str, str] = dataclasses.field(default_factory=dict)
     """ Generated source code (only filled when debug is True) """
 
-    union_se_args: Dict[str, List[Type[Any]]] = dataclasses.field(default_factory=dict)
+    union_se_args: dict[str, list[type[Any]]] = dataclasses.field(default_factory=dict)
     """ The union serializing functions need references to their types """
 
     reuse_instances_default: bool = True
@@ -266,7 +256,7 @@ class Scope:
     convert_sets_default: bool = False
 
     def __repr__(self) -> str:
-        res: List[str] = []
+        res: list[str] = []
 
         res.append("==================================================")
         res.append(self._justify(self.cls.__name__))
@@ -317,7 +307,7 @@ def raise_unsupported_type(obj: Any) -> None:
 
 
 def gen(
-    code: str, globals: Optional[Dict[str, Any]] = None, locals: Optional[Dict[str, Any]] = None
+    code: str, globals: Optional[dict[str, Any]] = None, locals: Optional[dict[str, Any]] = None
 ) -> str:
     """
     A wrapper of builtin `exec` function.
@@ -334,7 +324,7 @@ def gen(
     return code
 
 
-def add_func(serde_scope: Scope, func_name: str, func_code: str, globals: Dict[str, Any]) -> None:
+def add_func(serde_scope: Scope, func_name: str, func_code: str, globals: dict[str, Any]) -> None:
     """
     Generate a function and add it to a Scope's `funcs` dictionary.
 
@@ -354,7 +344,7 @@ def add_func(serde_scope: Scope, func_name: str, func_code: str, globals: Dict[s
 def is_instance(obj: Any, typ: Any) -> bool:
     """
     Type check function that works like `isinstance` but it accepts
-    Subscripted Generics e.g. `List[int]`.
+    Subscripted Generics e.g. `list[int]`.
     """
     if dataclasses.is_dataclass(typ):
         return isinstance(obj, typ)
@@ -388,21 +378,21 @@ def is_instance(obj: Any, typ: Any) -> bool:
         return isinstance(obj, typ)
 
 
-def is_opt_instance(obj: Any, typ: Type[Any]) -> bool:
+def is_opt_instance(obj: Any, typ: type[Any]) -> bool:
     if obj is None:
         return True
     opt_arg = type_args(typ)[0]
     return is_instance(obj, opt_arg)
 
 
-def is_union_instance(obj: Any, typ: Type[Any]) -> bool:
+def is_union_instance(obj: Any, typ: type[Any]) -> bool:
     for arg in type_args(typ):
         if is_instance(obj, arg):
             return True
     return False
 
 
-def is_list_instance(obj: Any, typ: Type[Any]) -> bool:
+def is_list_instance(obj: Any, typ: type[Any]) -> bool:
     if not isinstance(obj, list):
         return False
     if len(obj) == 0 or is_bare_list(typ):
@@ -412,7 +402,7 @@ def is_list_instance(obj: Any, typ: Type[Any]) -> bool:
     return is_instance(obj[0], list_arg)
 
 
-def is_set_instance(obj: Any, typ: Type[Any]) -> bool:
+def is_set_instance(obj: Any, typ: type[Any]) -> bool:
     if not isinstance(obj, (set, frozenset)):
         return False
     if len(obj) == 0 or is_bare_set(typ):
@@ -422,7 +412,7 @@ def is_set_instance(obj: Any, typ: Type[Any]) -> bool:
     return is_instance(next(iter(obj)), set_arg)
 
 
-def is_tuple_instance(obj: Any, typ: Type[Any]) -> bool:
+def is_tuple_instance(obj: Any, typ: type[Any]) -> bool:
     if not isinstance(obj, tuple):
         return False
     if is_variable_tuple(typ):
@@ -438,7 +428,7 @@ def is_tuple_instance(obj: Any, typ: Type[Any]) -> bool:
     return True
 
 
-def is_dict_instance(obj: Any, typ: Type[Any]) -> bool:
+def is_dict_instance(obj: Any, typ: type[Any]) -> bool:
     if not isinstance(obj, dict):
         return False
     if len(obj) == 0 or is_bare_dict(typ):
@@ -451,7 +441,7 @@ def is_dict_instance(obj: Any, typ: Type[Any]) -> bool:
     return False
 
 
-def is_generic_instance(obj: Any, typ: Type[Any]) -> bool:
+def is_generic_instance(obj: Any, typ: type[Any]) -> bool:
     return is_instance(obj, get_origin(typ))
 
 
@@ -501,7 +491,7 @@ class FlattenOpts:
 def field(
     *args: Any,
     rename: Optional[str] = None,
-    alias: Optional[List[str]] = None,
+    alias: Optional[list[str]] = None,
     skip: Optional[bool] = None,
     skip_if: Optional[Callable[[Any], Any]] = None,
     skip_if_false: Optional[bool] = None,
@@ -509,7 +499,7 @@ def field(
     serializer: Optional[Callable[..., Any]] = None,
     deserializer: Optional[Callable[..., Any]] = None,
     flatten: Optional[Union[FlattenOpts, bool]] = None,
-    metadata: Optional[Dict[str, Any]] = None,
+    metadata: Optional[dict[str, Any]] = None,
     **kwargs: Any,
 ) -> Any:
     """
@@ -550,7 +540,7 @@ class Field(Generic[T]):
     `type`, `name`, `default` and `default_factory` are the same members as `dataclasses.Field`.
     """
 
-    type: Type[T]
+    type: type[T]
     """ Type of Field """
     name: Optional[str]
     """ Name of Field """
@@ -565,7 +555,7 @@ class Field(Generic[T]):
     metadata: Mapping[str, Any] = field(default_factory=dict)
     kw_only: bool = False
     case: Optional[str] = None
-    alias: List[str] = field(default_factory=list)
+    alias: list[str] = field(default_factory=list)
     rename: Optional[str] = None
     skip: Optional[bool] = None
     skip_if: Optional[Func] = None
@@ -574,11 +564,11 @@ class Field(Generic[T]):
     serializer: Optional[Func] = None  # Custom field serializer.
     deserializer: Optional[Func] = None  # Custom field deserializer.
     flatten: Optional[FlattenOpts] = None
-    parent: Optional[Type[Any]] = None
-    type_args: Optional[List[str]] = None
+    parent: Optional[Any] = None
+    type_args: Optional[list[str]] = None
 
     @classmethod
-    def from_dataclass(cls, f: DataclassField, parent: Optional[Type[Any]] = None) -> Field[T]:
+    def from_dataclass(cls, f: dataclasses.Field[T], parent: Optional[Any] = None) -> Field[T]:
         """
         Create `Field` object from `dataclasses.Field`.
         """
@@ -634,7 +624,7 @@ class Field(Generic[T]):
             kw_only=kw_only,
         )
 
-    def to_dataclass(self) -> DataclassField:
+    def to_dataclass(self) -> dataclasses.Field[T]:
         f = dataclasses.Field(
             default=self.default,
             default_factory=self.default_factory,
@@ -655,10 +645,10 @@ class Field(Generic[T]):
             return False
         if self.parent is None:
             return False
-        return self.type == self.parent
+        return self.type == self.parent  # type: ignore
 
     @staticmethod
-    def mangle(field: DataclassField, name: str) -> str:
+    def mangle(field: dataclasses.Field[Any], name: str) -> str:
         """
         Get mangled name based on field name.
         """
@@ -680,7 +670,7 @@ class Field(Generic[T]):
 F = TypeVar("F", bound=Field[Any])
 
 
-def fields(field_cls: Type[F], cls: Type[Any], serialize_class_var: bool = False) -> List[F]:
+def fields(field_cls: type[F], cls: type[Any], serialize_class_var: bool = False) -> list[F]:
     """
     Iterate fields of the dataclass and returns `serde.core.Field`.
     """
@@ -721,9 +711,8 @@ def union_func_name(prefix: str, union_args: Sequence[Any]) -> str:
     * `union_args`: type arguments of a Union
 
     >>> from ipaddress import IPv4Address
-    >>> from typing import List
-    >>> union_func_name("union_se", [int, List[str], IPv4Address])
-    'union_se_int_List_str__IPv4Address'
+    >>> union_func_name("union_se", [int, list[str], IPv4Address])
+    'union_se_int_list_str__IPv4Address'
     """
     return re.sub(r"[^A-Za-z0-9]", "_", f"{prefix}_{'_'.join([typename(e) for e in union_args])}")
 
@@ -775,7 +764,7 @@ class Tagging:
         return self.kind == self.Kind.Untagged
 
     @classmethod
-    def is_taggable(cls, typ: Type[Any]) -> bool:
+    def is_taggable(cls, typ: type[Any]) -> bool:
         return dataclasses.is_dataclass(typ)
 
     def check(self) -> None:
@@ -856,7 +845,7 @@ def ensure(expr: Any, description: str) -> None:
         raise Exception(description)
 
 
-def should_impl_dataclass(cls: Type[Any]) -> bool:
+def should_impl_dataclass(cls: type[Any]) -> bool:
     """
     Test if class doesn't have @dataclass.
 
@@ -925,11 +914,11 @@ coerce = TypeCheck(kind=TypeCheck.Kind.Coerce)
 strict = TypeCheck(kind=TypeCheck.Kind.Strict)
 
 
-def coerce_object(typ: Type[Any], obj: Any) -> Any:
+def coerce_object(typ: type[Any], obj: Any) -> Any:
     return typ(obj) if is_coercible(typ, obj) else obj
 
 
-def is_coercible(typ: Type[Any], obj: Any) -> bool:
+def is_coercible(typ: type[Any], obj: Any) -> bool:
     if obj is None:
         return False
     return True
@@ -955,11 +944,10 @@ def has_default_factory(field: Field[Any]) -> bool:
     """
     Test if the field has default factory.
 
-    >>> from typing import Dict
     >>> @dataclasses.dataclass
     ... class C:
     ...     a: int
-    ...     d: Dict = dataclasses.field(default_factory=dict)
+    ...     d: dict = dataclasses.field(default_factory=dict)
     >>> has_default_factory(dataclasses.fields(C)[0])
     False
     >>> has_default_factory(dataclasses.fields(C)[1])
@@ -998,7 +986,7 @@ class ClassDeserializer(Protocol):
     >>> from plum import dispatch
     >>> class MyDeserializer(ClassDeserializer):
     ...     @dispatch
-    ...     def deserialize(self, cls: Type[datetime], value: Any) -> datetime:
+    ...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
     ...         return datetime.strptime(value, "%d/%m/%y")
     """
 
@@ -1006,9 +994,9 @@ class ClassDeserializer(Protocol):
         pass
 
 
-GLOBAL_CLASS_SERIALIZER: List[ClassSerializer] = []
+GLOBAL_CLASS_SERIALIZER: list[ClassSerializer] = []
 
-GLOBAL_CLASS_DESERIALIZER: List[ClassDeserializer] = []
+GLOBAL_CLASS_DESERIALIZER: list[ClassDeserializer] = []
 
 
 def add_serializer(serializer: ClassSerializer) -> None:
diff --git a/serde/de.py b/serde/de.py
index cdaba7d..8af6ced 100644
--- a/serde/de.py
+++ b/serde/de.py
@@ -9,21 +9,14 @@ import itertools
 import collections
 import dataclasses
 import functools
+import typing
+import jinja2
 import beartype
-from beartype import typing
+from collections.abc import Callable, Sequence, Iterable
 from beartype.roar import BeartypeCallHintParamViolation
 from dataclasses import dataclass, is_dataclass
-from typing import overload, TypeVar, Generic, Any, Optional, Sequence, Iterable
-from beartype.typing import (
-    Callable,
-    Dict,
-    List,
-    Union,
-    Literal,
-)
-
-import jinja2
-from typing_extensions import Type, dataclass_transform
+from typing import overload, TypeVar, Generic, Any, Optional, Union, Literal
+from typing_extensions import dataclass_transform
 
 from .compat import (
     SerdeError,
@@ -102,12 +95,12 @@ from .numpy import (
 __all__ = ["deserialize", "is_deserializable", "from_dict", "from_tuple"]
 
 
-DeserializeFunc = Callable[[Type[Any], Any], Any]
+DeserializeFunc = Callable[[type[Any], Any], Any]
 """ Interface of Custom deserialize function. """
 
 
 def serde_legacy_custom_class_deserializer(
-    cls: Type[Any], datavar: Any, value: Any, custom: DeserializeFunc, default: Callable[[], Any]
+    cls: type[Any], datavar: Any, value: Any, custom: DeserializeFunc, default: Callable[[], Any]
 ) -> Any:
     """
     Handle custom deserialization. Use default deserialization logic if it receives `SerdeSkip`
@@ -125,7 +118,7 @@ def serde_legacy_custom_class_deserializer(
         return default()
 
 
-def default_deserializer(_cls: Type[Any], obj: Any) -> Any:
+def default_deserializer(_cls: type[Any], obj: Any) -> Any:
     """
     Marker function to tell serde to use the default deserializer. It's used when custom
     deserializer is specified at the class but you want to override a field with the default
@@ -134,7 +127,7 @@ def default_deserializer(_cls: Type[Any], obj: Any) -> Any:
 
 
 def _get_by_aliases(
-    d: Dict[str, str], aliases: List[str], raise_error: bool = True
+    d: dict[str, str], aliases: list[str], raise_error: bool = True
 ) -> Optional[str]:
     if not aliases:
         if raise_error:
@@ -147,7 +140,7 @@ def _get_by_aliases(
         return _get_by_aliases(d, aliases[1:], raise_error=raise_error)
 
 
-def _exists_by_aliases(d: Dict[str, str], aliases: List[str]) -> bool:
+def _exists_by_aliases(d: dict[str, str], aliases: list[str]) -> bool:
     for alias in aliases:
         if alias in d:
             return True
@@ -156,7 +149,7 @@ def _exists_by_aliases(d: Dict[str, str], aliases: List[str]) -> bool:
 
 def _make_deserialize(
     cls_name: str,
-    fields: List[Any],
+    fields: list[Any],
     *args: Any,
     rename_all: Optional[str] = None,
     reuse_instances_default: bool = True,
@@ -165,7 +158,7 @@ def _make_deserialize(
     type_check: TypeCheck = strict,
     class_deserializer: Optional[ClassDeserializer] = None,
     **kwargs: Any,
-) -> Type[Any]:
+) -> type[Any]:
     """
     Create a deserializable class programatically.
     """
@@ -188,7 +181,7 @@ GENERATION_STACK = []
 
 @dataclass_transform()
 def deserialize(
-    _cls: Optional[Type[T]] = None,
+    _cls: Optional[type[T]] = None,
     rename_all: Optional[str] = None,
     reuse_instances_default: bool = True,
     convert_sets_default: bool = False,
@@ -197,7 +190,7 @@ def deserialize(
     type_check: TypeCheck = strict,
     class_deserializer: Optional[ClassDeserializer] = None,
     **kwargs: Any,
-) -> Type[T]:
+) -> type[T]:
     """
     A dataclass with this decorator is deserializable from any of the data formats supported
     by pyserde.
@@ -218,7 +211,7 @@ def deserialize(
 
     stack = []
 
-    def wrap(cls: Type[T]) -> Type[T]:
+    def wrap(cls: type[T]) -> type[T]:
         if cls in stack:
             return cls
         stack.append(cls)
@@ -232,7 +225,7 @@ def deserialize(
         if type_check.is_strict():
             beartype.beartype(cls)
 
-        g: Dict[str, Any] = {}
+        g: dict[str, Any] = {}
 
         # Create a scope storage used by serde.
         # Each class should get own scope. Child classes can not share scope with parent class.
@@ -242,7 +235,7 @@ def deserialize(
             scope = Scope(cls, reuse_instances_default=reuse_instances_default)
             setattr(cls, SERDE_SCOPE, scope)
 
-        class_deserializers: List[ClassDeserializer] = list(
+        class_deserializers: list[ClassDeserializer] = list(
             itertools.chain(
                 GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else []
             )
@@ -368,7 +361,7 @@ def is_deserializable(instance_or_class: Any) -> bool:
     return hasattr(instance_or_class, SERDE_SCOPE)
 
 
-def is_dataclass_without_de(cls: Type[Any]) -> bool:
+def is_dataclass_without_de(cls: type[Any]) -> bool:
     if not dataclasses.is_dataclass(cls):
         return False
     if not hasattr(cls, SERDE_SCOPE):
@@ -399,10 +392,10 @@ class Deserializer(Generic[T], metaclass=abc.ABCMeta):
         raise NotImplementedError
 
 
-def from_obj(c: Type[T], o: Any, named: bool, reuse_instances: Optional[bool]) -> T:
+def from_obj(c: type[T], o: Any, named: bool, reuse_instances: Optional[bool]) -> T:
     """
     Deserialize from an object into an instance of the type specified as arg `c`.
-    `c` can be either primitive type, `List`, `Tuple`, `Dict` or `deserialize` class.
+    `c` can be either primitive type, `list`, `tuple`, `dict` or `deserialize` class.
     """
 
     res: Any
@@ -412,7 +405,7 @@ def from_obj(c: Type[T], o: Any, named: bool, reuse_instances: Optional[bool]) -
     if type(o) is c:
         return o
 
-    def deserializable_to_obj(cls: Type[T]) -> T:
+    def deserializable_to_obj(cls: type[T]) -> T:
         serde_scope: Scope = getattr(cls, SERDE_SCOPE)
         func_name = FROM_DICT if named else FROM_ITER
         res = serde_scope.funcs[func_name](
@@ -504,14 +497,14 @@ def from_obj(c: Type[T], o: Any, named: bool, reuse_instances: Optional[bool]) -
 
 
 @overload
-def from_dict(cls: Type[T], o: Dict[str, Any], reuse_instances: Optional[bool] = None) -> T: ...
+def from_dict(cls: type[T], o: dict[str, Any], reuse_instances: Optional[bool] = None) -> T: ...
 
 
 @overload
-def from_dict(cls: Any, o: Dict[str, Any], reuse_instances: Optional[bool] = None) -> Any: ...
+def from_dict(cls: Any, o: dict[str, Any], reuse_instances: Optional[bool] = None) -> Any: ...
 
 
-def from_dict(cls: Any, o: Dict[str, Any], reuse_instances: Optional[bool] = None) -> Any:
+def from_dict(cls: Any, o: dict[str, Any], reuse_instances: Optional[bool] = None) -> Any:
     """
     Deserialize dictionary into object.
 
@@ -529,14 +522,14 @@ def from_dict(cls: Any, o: Dict[str, Any], reuse_instances: Optional[bool] = Non
 
     >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
     ...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
-    >>> from_dict(List[Foo], lst)
+    >>> from_dict(list[Foo], lst)
     [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
     """
     return from_obj(cls, o, named=True, reuse_instances=reuse_instances)
 
 
 @overload
-def from_tuple(cls: Type[T], o: Any, reuse_instances: Optional[bool] = None) -> T: ...
+def from_tuple(cls: type[T], o: Any, reuse_instances: Optional[bool] = None) -> T: ...
 
 
 @overload
@@ -560,7 +553,7 @@ def from_tuple(cls: Any, o: Any, reuse_instances: Optional[bool] = None) -> Any:
     You can pass any type supported by pyserde. For example,
 
     >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
-    >>> from_tuple(List[Foo], lst)
+    >>> from_tuple(list[Foo], lst)
     [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
     """
     return from_obj(cls, o, named=False, reuse_instances=reuse_instances)
@@ -585,11 +578,11 @@ class DeField(Field[T]):
         """
         Get inner `Field` from current `Field`.
 
-        `InnerField` is returned if self is of any standard collection e.g. List.
+        `InnerField` is returned if self is of any standard collection e.g. list.
         `DeField` is returned if self is Optional.
         """
         typ = type_args(self.type)[n]
-        opts: Dict[str, Any] = {
+        opts: dict[str, Any] = {
             "case": self.case,
             "rename": self.rename,
             "skip": self.skip,
@@ -616,7 +609,7 @@ class DeField(Field[T]):
 
     def key_field(self) -> DeField[Any]:
         """
-        Get inner key field for Dict like class.
+        Get inner key field for dict like class.
         """
         k = self[0]
         k.name = "k"
@@ -625,7 +618,7 @@ class DeField(Field[T]):
 
     def value_field(self) -> DeField[Any]:
         """
-        Get inner value field for Dict like class.
+        Get inner value field for dict like class.
         """
         return self[1]
 
@@ -634,7 +627,7 @@ class DeField(Field[T]):
         """
         Renders the variable name that possesses the data.
 
-        e.g. Tuple
+        e.g. tuple
             * datavar property returns "data"
             * data property returns "data[0]".
         e.g. Optional
@@ -670,8 +663,8 @@ class InnerField(DeField[T]):
     for inner type codegen.
 
     e.g.
-      T of List[T]
-      V of Dict[K, V]
+      T of list[T]
+      V of dict[K, V]
       T of Optional[T]
     """
 
@@ -684,7 +677,7 @@ class InnerField(DeField[T]):
         self.datavar = d
 
 
-def defields(cls: Type[Any]) -> List[DeField[Any]]:
+def defields(cls: type[Any]) -> list[DeField[Any]]:
     return fields(DeField, cls)
 
 
@@ -695,7 +688,7 @@ class Renderer:
     """
 
     func: str
-    cls: Optional[Type[Any]] = None
+    cls: Optional[type[Any]] = None
     legacy_class_deserializer: Optional[DeserializeFunc] = None
     import_numpy: bool = False
     suppress_coerce: bool = False
@@ -706,7 +699,7 @@ class Renderer:
         """
         Render rvalue
         """
-        implemented_methods: Dict[Type[Any], int] = {}
+        implemented_methods: dict[type[Any], int] = {}
         class_deserializers: Iterable[ClassDeserializer] = itertools.chain(
             GLOBAL_CLASS_DESERIALIZER, [self.class_deserializer] if self.class_deserializer else []
         )
@@ -845,19 +838,18 @@ class Renderer:
         """
         Render rvalue for Optional.
 
-        >>> from typing import List
         >>> Renderer('foo').render(DeField(Optional[int], 'o', datavar='data'))
         '(coerce_object(int, data["o"])) if data.get("o") is not None else None'
 
-        >>> Renderer('foo').render(DeField(Optional[List[int]], 'o', datavar='data'))
+        >>> Renderer('foo').render(DeField(Optional[list[int]], 'o', datavar='data'))
         '([coerce_object(int, v) for v in data["o"]]) if data.get("o") is not None else None'
 
-        >>> Renderer('foo').render(DeField(Optional[List[int]], 'o', datavar='data'))
+        >>> Renderer('foo').render(DeField(Optional[list[int]], 'o', datavar='data'))
         '([coerce_object(int, v) for v in data["o"]]) if data.get("o") is not None else None'
 
         >>> @deserialize
         ... class Foo:
-        ...     o: Optional[List[int]]
+        ...     o: Optional[list[int]]
         >>> Renderer('foo').render(DeField(Optional[Foo], 'f', datavar='data'))
         '(Foo.__serde__.funcs[\\'foo\\'](data=data["f"], maybe_generic=maybe_generic, \
 maybe_generic_type_vars=maybe_generic_type_vars, variable_type_args=None, \
@@ -880,11 +872,10 @@ reuse_instances=reuse_instances)) if data.get("f") is not None else None'
         """
         Render rvalue for list.
 
-        >>> from typing import List
-        >>> Renderer('foo').render(DeField(List[int], 'l', datavar='data'))
+        >>> Renderer('foo').render(DeField(list[int], 'l', datavar='data'))
         '[coerce_object(int, v) for v in data["l"]]'
 
-        >>> Renderer('foo').render(DeField(List[List[int]], 'l', datavar='data'))
+        >>> Renderer('foo').render(DeField(list[list[int]], 'l', datavar='data'))
         '[[coerce_object(int, v) for v in v] for v in data["l"]]'
         """
         if is_bare_list(arg.type):
@@ -914,17 +905,16 @@ reuse_instances=reuse_instances)) if data.get("f") is not None else None'
         """
         Render rvalue for tuple.
 
-        >>> from typing import List, Tuple
         >>> @deserialize
         ... class Foo: pass
-        >>> Renderer('foo').render(DeField(Tuple[str, int, List[int], Foo], 'd', datavar='data'))
+        >>> Renderer('foo').render(DeField(tuple[str, int, list[int], Foo], 'd', datavar='data'))
         '(coerce_object(str, data["d"][0]), coerce_object(int, data["d"][1]), \
 [coerce_object(int, v) for v in data["d"][2]], \
 Foo.__serde__.funcs[\\'foo\\'](data=data["d"][3], maybe_generic=maybe_generic, \
 maybe_generic_type_vars=maybe_generic_type_vars, variable_type_args=None, \
 reuse_instances=reuse_instances),)'
 
-        >>> field = DeField(Tuple[str, int, List[int], Foo],
+        >>> field = DeField(tuple[str, int, list[int], Foo],
         ...                'd',
         ...                 datavar='data',
         ...                 index=0,
@@ -952,13 +942,12 @@ variable_type_args=None, reuse_instances=reuse_instances),)"
         """
         Render rvalue for dict.
 
-        >>> from typing import List
-        >>> Renderer('foo').render(DeField(Dict[str, int], 'd', datavar='data'))
+        >>> Renderer('foo').render(DeField(dict[str, int], 'd', datavar='data'))
         '{coerce_object(str, k): coerce_object(int, v) for k, v in data["d"].items()}'
 
         >>> @deserialize
         ... class Foo: pass
-        >>> Renderer('foo').render(DeField(Dict[Foo, List[Foo]], 'f', datavar='data'))
+        >>> Renderer('foo').render(DeField(dict[Foo, list[Foo]], 'f', datavar='data'))
         '\
 {Foo.__serde__.funcs[\\'foo\\'](data=k, maybe_generic=maybe_generic, \
 maybe_generic_type_vars=maybe_generic_type_vars, variable_type_args=None, \
@@ -975,8 +964,8 @@ for k, v in data["f"].items()}'
             v = arg.value_field()
             origin = get_origin(v.type)
             if origin:
-                # When the callable type is of generic type e.g List.
-                # Get origin type "list" from "List[X]".
+                # When the callable type is of generic type e.g list.
+                # Get origin type "list" from "list[X]".
                 callable = origin.__name__
             else:
                 # When the callable type is non generic type e.g int, Foo.
@@ -1074,7 +1063,7 @@ def renderable(f: DeField[Any]) -> bool:
 
 
 def render_from_iter(
-    cls: Type[Any],
+    cls: type[Any],
     legacy_class_deserializer: Optional[DeserializeFunc] = None,
     type_check: TypeCheck = strict,
     class_deserializer: Optional[ClassDeserializer] = None,
@@ -1128,7 +1117,7 @@ def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=Non
 
 
 def render_from_dict(
-    cls: Type[Any],
+    cls: type[Any],
     rename_all: Optional[str] = None,
     legacy_class_deserializer: Optional[DeserializeFunc] = None,
     type_check: TypeCheck = strict,
@@ -1188,7 +1177,7 @@ def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=Non
 
 
 def render_union_func(
-    cls: Type[Any], union_args: Sequence[Type[Any]], tagging: Tagging = DefaultTagging
+    cls: type[Any], union_args: Sequence[type[Any]], tagging: Tagging = DefaultTagging
 ) -> str:
     template = """
 def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=None,
@@ -1249,7 +1238,7 @@ def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=Non
 
 
 def render_literal_func(
-    cls: Type[Any], literal_args: Sequence[Any], tagging: Tagging = DefaultTagging
+    cls: type[Any], literal_args: Sequence[Any], tagging: Tagging = DefaultTagging
 ) -> str:
     template = """
 def {{func}}(cls=cls, maybe_generic=None, maybe_generic_type_vars=None, data=None,
diff --git a/serde/inspect.py b/serde/inspect.py
index 2c9900d..1812324 100644
--- a/serde/inspect.py
+++ b/serde/inspect.py
@@ -19,14 +19,13 @@ import os
 import sys
 
 from typing import Any
-from typing_extensions import Type
 
 from .core import SERDE_SCOPE, init, logger
 
 init(True)
 
 
-def inspect(cls: Type[Any]) -> None:
+def inspect(cls: type[Any]) -> None:
     """
     Inspect a pyserde class.
     """
diff --git a/serde/json.py b/serde/json.py
index 427b7ab..47ec1d1 100644
--- a/serde/json.py
+++ b/serde/json.py
@@ -4,8 +4,6 @@ Serialize and Deserialize in JSON format.
 
 from typing import Any, AnyStr, overload, Optional, Union
 
-from typing_extensions import Type
-
 from .compat import T
 from .de import Deserializer, from_dict
 from .se import Serializer, to_dict
@@ -55,7 +53,7 @@ class JsonDeserializer(Deserializer[AnyStr]):
 def to_json(
     obj: Any,
     cls: Optional[Any] = None,
-    se: Type[Serializer[str]] = JsonSerializer,
+    se: type[Serializer[str]] = JsonSerializer,
     reuse_instances: bool = False,
     convert_sets: bool = True,
     **opts: Any,
@@ -80,19 +78,19 @@ def to_json(
 
 @overload
 def from_json(
-    c: Type[T], s: AnyStr, de: Type[Deserializer[AnyStr]] = JsonDeserializer, **opts: Any
+    c: type[T], s: AnyStr, de: type[Deserializer[AnyStr]] = JsonDeserializer, **opts: Any
 ) -> T: ...
 
 
 # For Union, Optional etc.
 @overload
 def from_json(
-    c: Any, s: AnyStr, de: Type[Deserializer[AnyStr]] = JsonDeserializer, **opts: Any
+    c: Any, s: AnyStr, de: type[Deserializer[AnyStr]] = JsonDeserializer, **opts: Any
 ) -> Any: ...
 
 
 def from_json(
-    c: Any, s: AnyStr, de: Type[Deserializer[AnyStr]] = JsonDeserializer, **opts: Any
+    c: Any, s: AnyStr, de: type[Deserializer[AnyStr]] = JsonDeserializer, **opts: Any
 ) -> Any:
     """
     Deserialize from JSON into the object. [orjson](https://github.com/ijl/orjson) will be used
diff --git a/serde/msgpack.py b/serde/msgpack.py
index e0b1baa..8e02d84 100644
--- a/serde/msgpack.py
+++ b/serde/msgpack.py
@@ -3,8 +3,7 @@ Serialize and Deserialize in MsgPack format. This module depends on
 [msgpack](https://pypi.org/project/msgpack/) package.
 """
 
-from typing import Any, Type, Optional, overload
-from beartype.typing import Dict
+from typing import Any, Optional, overload
 
 import msgpack
 
@@ -43,9 +42,9 @@ class MsgPackDeserializer(Deserializer[bytes]):
 def to_msgpack(
     obj: Any,
     cls: Optional[Any] = None,
-    se: Type[Serializer[bytes]] = MsgPackSerializer,
+    se: type[Serializer[bytes]] = MsgPackSerializer,
     named: bool = True,
-    ext_dict: Optional[Dict[Type[Any], int]] = None,
+    ext_dict: Optional[dict[type[Any], int]] = None,
     reuse_instances: bool = False,
     convert_sets: bool = True,
     **opts: Any,
@@ -82,11 +81,11 @@ def to_msgpack(
 
 @overload
 def from_msgpack(
-    c: Type[T],
+    c: type[T],
     s: bytes,
-    de: Type[Deserializer[bytes]] = MsgPackDeserializer,
+    de: type[Deserializer[bytes]] = MsgPackDeserializer,
     named: bool = True,
-    ext_dict: Optional[Dict[int, Type[Any]]] = None,
+    ext_dict: Optional[dict[int, type[Any]]] = None,
     **opts: Any,
 ) -> T: ...
 
@@ -95,9 +94,9 @@ def from_msgpack(
 def from_msgpack(
     c: Any,
     s: bytes,
-    de: Type[Deserializer[bytes]] = MsgPackDeserializer,
+    de: type[Deserializer[bytes]] = MsgPackDeserializer,
     named: bool = True,
-    ext_dict: Optional[Dict[int, Type[Any]]] = None,
+    ext_dict: Optional[dict[int, type[Any]]] = None,
     **opts: Any,
 ) -> Any: ...
 
@@ -105,9 +104,9 @@ def from_msgpack(
 def from_msgpack(
     c: Any,
     s: bytes,
-    de: Type[Deserializer[bytes]] = MsgPackDeserializer,
+    de: type[Deserializer[bytes]] = MsgPackDeserializer,
     named: bool = True,
-    ext_dict: Optional[Dict[int, Type[Any]]] = None,
+    ext_dict: Optional[dict[int, type[Any]]] = None,
     **opts: Any,
 ) -> Any:
     """
diff --git a/serde/numpy.py b/serde/numpy.py
index 9e35b65..a019a93 100644
--- a/serde/numpy.py
+++ b/serde/numpy.py
@@ -1,4 +1,4 @@
-from beartype.typing import Callable
+from collections.abc import Callable
 from typing import Any, Optional
 
 from serde.compat import get_args, get_origin
diff --git a/serde/pickle.py b/serde/pickle.py
index 34f53f7..dda5445 100644
--- a/serde/pickle.py
+++ b/serde/pickle.py
@@ -3,7 +3,7 @@ Serialize and Deserialize in Pickle format.
 """
 
 import pickle
-from typing import overload, Type, Any, Optional
+from typing import overload, Any, Optional
 
 from .compat import T
 from .de import Deserializer, from_dict
@@ -27,7 +27,7 @@ class PickleDeserializer(Deserializer[bytes]):
 def to_pickle(
     obj: Any,
     cls: Optional[Any] = None,
-    se: Type[Serializer[bytes]] = PickleSerializer,
+    se: type[Serializer[bytes]] = PickleSerializer,
     reuse_instances: bool = False,
     convert_sets: bool = True,
     **opts: Any,
@@ -39,18 +39,18 @@ def to_pickle(
 
 @overload
 def from_pickle(
-    c: Type[T], data: bytes, de: Type[Deserializer[bytes]] = PickleDeserializer, **opts: Any
+    c: type[T], data: bytes, de: type[Deserializer[bytes]] = PickleDeserializer, **opts: Any
 ) -> T: ...
 
 
 @overload
 def from_pickle(
-    c: Any, data: bytes, de: Type[Deserializer[bytes]] = PickleDeserializer, **opts: Any
+    c: Any, data: bytes, de: type[Deserializer[bytes]] = PickleDeserializer, **opts: Any
 ) -> Any: ...
 
 
 # For Union, Optional etc.
 def from_pickle(
-    c: Any, data: bytes, de: Type[Deserializer[bytes]] = PickleDeserializer, **opts: Any
+    c: Any, data: bytes, de: type[Deserializer[bytes]] = PickleDeserializer, **opts: Any
 ) -> Any:
     return from_dict(c, de.deserialize(data, **opts), reuse_instances=False)
diff --git a/serde/se.py b/serde/se.py
index 4946fe4..72274be 100644
--- a/serde/se.py
+++ b/serde/se.py
@@ -8,22 +8,14 @@ import abc
 import copy
 import dataclasses
 import functools
-from beartype import typing
+import typing
 import itertools
 import beartype
+import jinja2
 from dataclasses import dataclass, is_dataclass
-from typing import TypeVar, Literal, Generic, Optional, Any, Iterable, Iterator
-from beartype.typing import (
-    Callable,
-    Dict,
-    List,
-    Tuple,
-    Type,
-    Union,
-)
+from typing import TypeVar, Literal, Generic, Optional, Any, Union
+from collections.abc import Callable, Iterable, Iterator
 from beartype.door import is_bearable
-
-import jinja2
 from typing_extensions import dataclass_transform
 
 from .compat import (
@@ -95,11 +87,11 @@ from .numpy import (
 __all__ = ["serialize", "is_serializable", "to_dict", "to_tuple"]
 
 
-SerializeFunc = Callable[[Type[Any], Any], Any]
+SerializeFunc = Callable[[type[Any], Any], Any]
 """ Interface of Custom serialize function. """
 
 
-def default_serializer(_cls: Type[Any], obj: Any) -> Any:
+def default_serializer(_cls: type[Any], obj: Any) -> Any:
     """
     Marker function to tell serde to use the default serializer. It's used when custom serializer
     is specified at the class but you want to override a field with the default serializer.
@@ -107,7 +99,7 @@ def default_serializer(_cls: Type[Any], obj: Any) -> Any:
 
 
 def serde_legacy_custom_class_serializer(
-    cls: Type[Any], obj: Any, custom: SerializeFunc, default: Callable[[], Any]
+    cls: type[Any], obj: Any, custom: SerializeFunc, default: Callable[[], Any]
 ) -> Any:
     try:
         return custom(cls, obj)
@@ -130,7 +122,7 @@ class Serializer(Generic[T], metaclass=abc.ABCMeta):
 
 def _make_serialize(
     cls_name: str,
-    fields: Iterable[Union[str, Tuple[str, Type[Any]], Tuple[str, Type[Any], Any]]],
+    fields: Iterable[Union[str, tuple[str, type[Any]], tuple[str, type[Any], Any]]],
     *args: Any,
     rename_all: Optional[str] = None,
     reuse_instances_default: bool = False,
@@ -141,7 +133,7 @@ def _make_serialize(
     serialize_class_var: bool = False,
     class_serializer: Optional[ClassSerializer] = None,
     **kwargs: Any,
-) -> Type[Any]:
+) -> type[Any]:
     """
     Create a serializable class programatically.
     """
@@ -168,7 +160,7 @@ GENERATION_STACK = []
 
 @dataclass_transform()
 def serialize(
-    _cls: Optional[Type[T]] = None,
+    _cls: Optional[type[T]] = None,
     rename_all: Optional[str] = None,
     reuse_instances_default: bool = False,
     convert_sets_default: bool = False,
@@ -178,7 +170,7 @@ def serialize(
     serialize_class_var: bool = False,
     class_serializer: Optional[ClassSerializer] = None,
     **kwargs: Any,
-) -> Type[T]:
+) -> type[T]:
     """
     A dataclass with this decorator is serializable into any of the data formats
     supported by pyserde.
@@ -198,7 +190,7 @@ def serialize(
     '{"i":10,"s":"foo","f":100.0,"b":true}'
     """
 
-    def wrap(cls: Type[T]) -> Type[T]:
+    def wrap(cls: type[T]) -> type[T]:
         tagging.check()
 
         # If no `dataclass` found in the class, dataclassify it automatically.
@@ -208,7 +200,7 @@ def serialize(
         if type_check.is_strict():
             beartype.beartype(cls)
 
-        g: Dict[str, Any] = {}
+        g: dict[str, Any] = {}
 
         # Create a scope storage used by serde.
         # Each class should get own scope. Child classes can not share scope with parent class.
@@ -222,7 +214,7 @@ def serialize(
             )
             setattr(cls, SERDE_SCOPE, scope)
 
-        class_serializers: List[ClassSerializer] = list(
+        class_serializers: list[ClassSerializer] = list(
             itertools.chain(GLOBAL_CLASS_SERIALIZER, [class_serializer] if class_serializer else [])
         )
 
@@ -325,7 +317,7 @@ def is_serializable(instance_or_class: Any) -> bool:
     return hasattr(instance_or_class, SERDE_SCOPE)
 
 
-def is_dataclass_without_se(cls: Type[Any]) -> bool:
+def is_dataclass_without_se(cls: type[Any]) -> bool:
     if not dataclasses.is_dataclass(cls):
         return False
     if not hasattr(cls, SERDE_SCOPE):
@@ -393,7 +385,7 @@ def to_obj(
         raise SerdeError(e) from None
 
 
-def astuple(v: Any) -> Tuple[Any, ...]:
+def astuple(v: Any) -> tuple[Any, ...]:
     """
     Serialize object into tuple.
     """
@@ -402,10 +394,10 @@ def astuple(v: Any) -> Tuple[Any, ...]:
 
 def to_tuple(
     o: Any,
-    c: Optional[Type[Any]] = None,
+    c: Optional[type[Any]] = None,
     reuse_instances: Optional[bool] = None,
     convert_sets: Optional[bool] = None,
-) -> Tuple[Any, ...]:
+) -> tuple[Any, ...]:
     """
     Serialize object into tuple.
 
@@ -430,7 +422,7 @@ def to_tuple(
     )
 
 
-def asdict(v: Any) -> Dict[Any, Any]:
+def asdict(v: Any) -> dict[Any, Any]:
     """
     Serialize object into dictionary.
     """
@@ -439,10 +431,10 @@ def asdict(v: Any) -> Dict[Any, Any]:
 
 def to_dict(
     o: Any,
-    c: Optional[Type[Any]] = None,
+    c: Optional[type[Any]] = None,
     reuse_instances: Optional[bool] = None,
     convert_sets: Optional[bool] = None,
-) -> Dict[Any, Any]:
+) -> dict[Any, Any]:
     """
     Serialize object into dictionary.
 
@@ -491,7 +483,7 @@ class SeField(Field[T]):
         return SeField(typ, name=None)
 
 
-def sefields(cls: Type[Any], serialize_class_var: bool = False) -> Iterator[SeField[Any]]:
+def sefields(cls: type[Any], serialize_class_var: bool = False) -> Iterator[SeField[Any]]:
     """
     Iterate fields for serialization.
     """
@@ -501,7 +493,7 @@ def sefields(cls: Type[Any], serialize_class_var: bool = False) -> Iterator[SeFi
 
 
 def render_to_tuple(
-    cls: Type[Any],
+    cls: type[Any],
     legacy_class_serializer: Optional[SerializeFunc] = None,
     type_check: TypeCheck = strict,
     serialize_class_var: bool = False,
@@ -543,7 +535,7 @@ def {{func}}(obj, reuse_instances=None, convert_sets=None):
 
 
 def render_to_dict(
-    cls: Type[Any],
+    cls: type[Any],
     case: Optional[str] = None,
     legacy_class_serializer: Optional[SerializeFunc] = None,
     type_check: TypeCheck = strict,
@@ -594,7 +586,7 @@ def {{func}}(obj, reuse_instances = None, convert_sets = None):
 
 
 def render_union_func(
-    cls: Type[Any], union_args: List[Type[Any]], tagging: Tagging = DefaultTagging
+    cls: type[Any], union_args: list[type[Any]], tagging: Tagging = DefaultTagging
 ) -> str:
     """
     Render function that serializes a field with union type.
@@ -690,11 +682,10 @@ class Renderer:
         """
         Render rvalue
 
-        >>> from typing import Tuple
         >>> Renderer(TO_ITER).render(SeField(int, 'i'))
         'coerce_object(int, i)'
 
-        >>> Renderer(TO_ITER).render(SeField(List[int], 'l'))
+        >>> Renderer(TO_ITER).render(SeField(list[int], 'l'))
         '[coerce_object(int, v) for v in l]'
 
         >>> @serialize
@@ -705,29 +696,29 @@ class Renderer:
         "\
 foo.__serde__.funcs['to_iter'](foo, reuse_instances=reuse_instances, convert_sets=convert_sets)"
 
-        >>> Renderer(TO_ITER).render(SeField(List[Foo], 'foo'))
+        >>> Renderer(TO_ITER).render(SeField(list[Foo], 'foo'))
         "\
 [v.__serde__.funcs['to_iter'](v, reuse_instances=reuse_instances, \
 convert_sets=convert_sets) for v in foo]"
 
-        >>> Renderer(TO_ITER).render(SeField(Dict[str, Foo], 'foo'))
+        >>> Renderer(TO_ITER).render(SeField(dict[str, Foo], 'foo'))
         "\
 {coerce_object(str, k): v.__serde__.funcs['to_iter'](v, reuse_instances=reuse_instances, \
 convert_sets=convert_sets) for k, v in foo.items()}"
 
-        >>> Renderer(TO_ITER).render(SeField(Dict[Foo, Foo], 'foo'))
+        >>> Renderer(TO_ITER).render(SeField(dict[Foo, Foo], 'foo'))
         "\
 {k.__serde__.funcs['to_iter'](k, reuse_instances=reuse_instances, \
 convert_sets=convert_sets): v.__serde__.funcs['to_iter'](v, reuse_instances=reuse_instances, \
 convert_sets=convert_sets) for k, v in foo.items()}"
 
-        >>> Renderer(TO_ITER).render(SeField(Tuple[str, Foo, int], 'foo'))
+        >>> Renderer(TO_ITER).render(SeField(tuple[str, Foo, int], 'foo'))
         "\
 (coerce_object(str, foo[0]), foo[1].__serde__.funcs['to_iter'](foo[1], \
 reuse_instances=reuse_instances, convert_sets=convert_sets), \
 coerce_object(int, foo[2]),)"
         """
-        implemented_methods: Dict[Type[Any], int] = {}
+        implemented_methods: dict[type[Any], int] = {}
         class_serializers: Iterable[ClassSerializer] = itertools.chain(
             GLOBAL_CLASS_SERIALIZER, [self.class_serializer] if self.class_serializer else []
         )
@@ -810,7 +801,7 @@ coerce_object(int, foo[2]),)"
         if arg.flatten:
             flattened = []
             for f in sefields(arg.type, self.serialize_class_var):
-                f.parent = arg  # type: ignore
+                f.parent = arg
                 flattened.append(self.render(f))
             return ", ".join(flattened)
         else:
diff --git a/serde/toml.py b/serde/toml.py
index e83f85e..2bb85ab 100644
--- a/serde/toml.py
+++ b/serde/toml.py
@@ -5,7 +5,7 @@ Serialize and Deserialize in TOML format. This module depends on
 """
 
 import sys
-from typing import Type, overload, Optional, Any
+from typing import overload, Optional, Any
 
 import tomli_w
 
@@ -37,7 +37,7 @@ class TomlDeserializer(Deserializer[str]):
 def to_toml(
     obj: Any,
     cls: Optional[Any] = None,
-    se: Type[Serializer[str]] = TomlSerializer,
+    se: type[Serializer[str]] = TomlSerializer,
     reuse_instances: bool = False,
     convert_sets: bool = True,
     **opts: Any,
@@ -58,18 +58,18 @@ def to_toml(
 
 @overload
 def from_toml(
-    c: Type[T], s: str, de: Type[Deserializer[str]] = TomlDeserializer, **opts: Any
+    c: type[T], s: str, de: type[Deserializer[str]] = TomlDeserializer, **opts: Any
 ) -> T: ...
 
 
 # For Union, Optional etc.
 @overload
 def from_toml(
-    c: Any, s: str, de: Type[Deserializer[str]] = TomlDeserializer, **opts: Any
+    c: Any, s: str, de: type[Deserializer[str]] = TomlDeserializer, **opts: Any
 ) -> Any: ...
 
 
-def from_toml(c: Any, s: str, de: Type[Deserializer[str]] = TomlDeserializer, **opts: Any) -> Any:
+def from_toml(c: Any, s: str, de: type[Deserializer[str]] = TomlDeserializer, **opts: Any) -> Any:
     """
     Deserialize from TOML into the object.
 
diff --git a/serde/yaml.py b/serde/yaml.py
index e21ab49..3e076a3 100644
--- a/serde/yaml.py
+++ b/serde/yaml.py
@@ -3,7 +3,7 @@ Serialize and Deserialize in YAML format. This module depends on
 [pyyaml](https://pypi.org/project/PyYAML/) package.
 """
 
-from typing import overload, Type, Optional, Any
+from typing import overload, Optional, Any
 
 import yaml
 
@@ -29,7 +29,7 @@ class YamlDeserializer(Deserializer[str]):
 def to_yaml(
     obj: Any,
     cls: Optional[Any] = None,
-    se: Type[Serializer[str]] = YamlSerializer,
+    se: type[Serializer[str]] = YamlSerializer,
     reuse_instances: bool = False,
     convert_sets: bool = True,
     **opts: Any,
@@ -50,18 +50,18 @@ def to_yaml(
 
 @overload
 def from_yaml(
-    c: Type[T], s: str, de: Type[Deserializer[str]] = YamlDeserializer, **opts: Any
+    c: type[T], s: str, de: type[Deserializer[str]] = YamlDeserializer, **opts: Any
 ) -> T: ...
 
 
 # For Union, Optional etc.
 @overload
 def from_yaml(
-    c: Any, s: str, de: Type[Deserializer[str]] = YamlDeserializer, **opts: Any
+    c: Any, s: str, de: type[Deserializer[str]] = YamlDeserializer, **opts: Any
 ) -> Any: ...
 
 
-def from_yaml(c: Any, s: str, de: Type[Deserializer[str]] = YamlDeserializer, **opts: Any) -> Any:
+def from_yaml(c: Any, s: str, de: type[Deserializer[str]] = YamlDeserializer, **opts: Any) -> Any:
     """
     `c` is a class obejct and `s` is YAML string. If you supply keyword arguments other than `de`,
     they will be passed in `yaml.safe_load` function.


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 49c9c6b..e0e47a7 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,7 +10,7 @@ jobs:
     strategy:
       matrix:
         # Remove pypy-3.8 until it supports numpy
-        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]  # "pypy-3.8"
+        python-version: ["3.9", "3.10", "3.11", "3.12"]  # "pypy-3.8"
     steps:
       - uses: actions/checkout@v4
       - name: Install poetry
diff --git a/tests/common.py b/tests/common.py
index 5defdb9..3722870 100644
--- a/tests/common.py
+++ b/tests/common.py
@@ -14,10 +14,6 @@ from typing import (
     TypeVar,
     Union,
     Callable,
-    Dict,
-    List,
-    Set,
-    Tuple,
 )
 from typing_extensions import TypeAlias
 
@@ -31,7 +27,7 @@ from serde.toml import from_toml, to_toml
 from serde.yaml import from_yaml, to_yaml
 from tests import data
 
-FormatFuncs: TypeAlias = List[Tuple[Callable[..., Any], Callable[..., Any]]]
+FormatFuncs: TypeAlias = list[tuple[Callable[..., Any], Callable[..., Any]]]
 
 format_dict: FormatFuncs = [(to_dict, from_dict)]
 
@@ -84,7 +80,7 @@ class NestedGenericClass(Generic[U, V]):
 Filter: TypeAlias = Callable[..., bool]
 
 
-def param(val: T, typ: U, filter: Optional[Filter] = None) -> Tuple[T, U, Filter]:
+def param(val: T, typ: U, filter: Optional[Filter] = None) -> tuple[T, U, Filter]:
     """
     Create a test parameter
 
@@ -103,30 +99,28 @@ def yaml_not_supported(se: Any, de: Any, opt: Any) -> bool:
     return se is to_yaml
 
 
-types: List[Tuple[Any, Any, Optional[Filter]]] = [
+types: list[tuple[Any, Any, Optional[Filter]]] = [
     param(10, int),  # Primitive
     param("foo", str),
     param(100.0, float),
     param(True, bool),
     param(10, Optional[int]),  # Optional
     param(None, Optional[int], toml_not_supported),
-    param([1, 2], List[int]),  # Container
-    param([1, 2], List),
+    param([1, 2], list[int]),  # Container
     param([1, 2], list),
-    param([], List[int]),
-    param({1, 2}, Set[int], toml_not_supported),
-    param({1, 2}, Set, toml_not_supported),
+    param([], list[int]),
+    param({1, 2}, set[int], toml_not_supported),
     param({1, 2}, set, toml_not_supported),
-    param(set(), Set[int], toml_not_supported),
+    param(set(), set[int], toml_not_supported),
     # TODO param(frozenset({1, 2}), FrozenSet[int], toml_not_supported),
-    param((1, 1), Tuple[int, int]),
-    param((1, 1), Tuple),
-    param((1, 2, 3), Tuple[int, ...]),
-    param({"a": 1}, Dict[str, int]),
-    param({"a": 1}, Dict),
+    param((1, 1), tuple[int, int]),
+    param((1, 1), tuple),
+    param((1, 2, 3), tuple[int, ...]),
+    param({"a": 1}, dict[str, int]),
     param({"a": 1}, dict),
-    param({}, Dict[str, int]),
-    param({"a": 1}, Dict[str, int]),
+    param({"a": 1}, dict),
+    param({}, dict[str, int]),
+    param({"a": 1}, dict[str, int]),
     # TODO param({"a": 1}, DefaultDict[str, int]),
     # TODO param({"a": [1]}, DefaultDict[str, List[int]]),
     param(data.Pri(10, "foo", 100.0, True), data.Pri),  # dataclass
@@ -180,18 +174,18 @@ if sys.version_info[:3] >= (3, 9, 0):
         [param([1, 2], list[int]), param({"a": 1}, dict[str, int]), param((1, 1), tuple[int, int])]
     )
 
-types_combinations: List[Any] = [
+types_combinations: list[Any] = [
     list(more_itertools.flatten(c)) for c in itertools.combinations(types, 2)
 ]
 
-opt_case: List[Dict[str, Union[bool, str]]] = [
+opt_case: list[dict[str, Union[bool, str]]] = [
     {"reuse_instances_default": False},
     {"reuse_instances_default": False, "rename_all": "camelcase"},
     {"reuse_instances_default": False, "rename_all": "snakecase"},
 ]
 
 
-def make_id_from_dict(d: Dict[str, Union[bool, str]]) -> str:
+def make_id_from_dict(d: dict[str, Union[bool, str]]) -> str:
     if not d:
         return "none"
     else:
@@ -199,33 +193,33 @@ def make_id_from_dict(d: Dict[str, Union[bool, str]]) -> str:
         return f"{key}-{d[key]}"
 
 
-def opt_case_ids() -> List[str]:
+def opt_case_ids() -> list[str]:
     """
     Create parametrize test id
     """
     return list(map(make_id_from_dict, opt_case))
 
 
-def type_ids() -> List[str]:
+def type_ids() -> list[str]:
     """
     Create parametrize test id
     """
     from serde.compat import typename
 
-    def make_id(pair: Tuple[Any, ...]) -> str:
+    def make_id(pair: tuple[Any, ...]) -> str:
         t, T, _ = pair
         return f"{typename(T)}({t})"
 
     return list(map(make_id, types))
 
 
-def type_combinations_ids() -> List[str]:
+def type_combinations_ids() -> list[str]:
     """
     Create parametrize test id
     """
     from serde.compat import typename
 
-    def make_id(quad: Tuple[Any, ...]) -> str:
+    def make_id(quad: tuple[Any, ...]) -> str:
         t, T, u, U = quad
         return f"{typename(T)}({t})-{typename(U)}({u})"
 
diff --git a/tests/data.py b/tests/data.py
index 416e3d9..e790b47 100644
--- a/tests/data.py
+++ b/tests/data.py
@@ -1,8 +1,7 @@
 from __future__ import annotations
-import enum
 from dataclasses import dataclass
+import enum
 from typing import Optional, Any
-from beartype.typing import Dict, List, Tuple
 
 from serde import field, serde, disabled
 
@@ -10,7 +9,6 @@ from . import imported
 
 
 @serde
-@dataclass
 class Inner:
     i: int
 
@@ -105,10 +103,10 @@ class PriList:
     List containing primitives.
     """
 
-    i: List[int]
-    s: List[str]
-    f: List[float]
-    b: List[bool]
+    i: list[int]
+    s: list[str]
+    f: list[float]
+    b: list[bool]
 
 
 @serde
@@ -117,10 +115,10 @@ class PriDict:
     Dict containing primitives.
     """
 
-    i: Dict[str, int]
-    s: Dict[str, str]
-    f: Dict[str, float]
-    b: Dict[str, bool]
+    i: dict[str, int]
+    s: dict[str, str]
+    f: dict[str, float]
+    b: dict[str, bool]
 
 
 @serde
@@ -129,10 +127,10 @@ class PriTuple:
     Tuple containing primitives.
     """
 
-    i: Tuple[int, int, int]
-    s: Tuple[str, str, str, str]
-    f: Tuple[float, float, float, float, float]
-    b: Tuple[bool, bool, bool, bool, bool, bool]
+    i: tuple[int, int, int]
+    s: tuple[str, str, str, str]
+    f: tuple[float, float, float, float, float]
+    b: tuple[bool, bool, bool, bool, bool, bool]
 
 
 @dataclass(unsafe_hash=True)
@@ -175,10 +173,10 @@ class NestedPriList:
     List containing nested primitives.
     """
 
-    i: List[Int]
-    s: List[Str]
-    f: List[Float]
-    b: List[Bool]
+    i: list[Int]
+    s: list[Str]
+    f: list[Float]
+    b: list[Bool]
 
 
 @serde
@@ -187,10 +185,10 @@ class NestedPriDict:
     Dict containing nested primitives.
     """
 
-    i: Dict[Str, Int]
-    s: Dict[Str, Str]
-    f: Dict[Str, Float]
-    b: Dict[Str, Bool]
+    i: dict[Str, Int]
+    s: dict[Str, Str]
+    f: dict[Str, Float]
+    b: dict[Str, Bool]
 
 
 @serde
@@ -199,10 +197,10 @@ class NestedPriTuple:
     Tuple containing nested primitives.
     """
 
-    i: Tuple[Int, Int, Int]
-    s: Tuple[Str, Str, Str, Str]
-    f: Tuple[Float, Float, Float, Float, Float]
-    b: Tuple[Bool, Bool, Bool, Bool, Bool, Bool]
+    i: tuple[Int, Int, Int]
+    s: tuple[Str, Str, Str, Str]
+    f: tuple[Float, Float, Float, Float, Float]
+    b: tuple[Bool, Bool, Bool, Bool, Bool, Bool]
 
 
 @serde
@@ -255,23 +253,23 @@ class EnumInClass:
 @dataclass(unsafe_hash=True)
 class Recur:
     a: Optional[Recur]
-    b: Optional[List[Recur]]
-    c: Optional[Dict[str, Recur]]
+    b: Optional[list[Recur]]
+    c: Optional[dict[str, Recur]]
 
 
 @dataclass(unsafe_hash=True)
 class RecurContainer:
-    a: List[RecurContainer]
-    b: Dict[str, RecurContainer]
+    a: list[RecurContainer]
+    b: dict[str, RecurContainer]
 
 
 serde(Recur)
 serde(RecurContainer)
 
 
-ListPri = List[Pri]
+ListPri = list[Pri]
 
-DictPri = Dict[str, Pri]
+DictPri = dict[str, Pri]
 
 INT = Int(10)
 
@@ -292,7 +290,6 @@ NESTED_PRILIST = ([INT], [STR], [FLOAT], [BOOL])
 NESTED_PRILIST_TUPLE = ([(10,)], [("foo",)], [(100.0,)], [(True,)])
 
 
-@dataclass
 @serde
 class Init:
     a: int
@@ -307,6 +304,5 @@ class StrSubclass(str):
 
 
 @serde
-@dataclass
 class PrimitiveSubclass:
     v: StrSubclass
diff --git a/tests/test_basics.py b/tests/test_basics.py
index f81b342..89f89ec 100644
--- a/tests/test_basics.py
+++ b/tests/test_basics.py
@@ -9,14 +9,7 @@ from typing import (
     Optional,
     Any,
 )
-from beartype.typing import (
-    DefaultDict,
-    Dict,
-    FrozenSet,
-    List,
-    Set,
-    Tuple,
-)
+from collections import defaultdict
 
 import pytest
 
@@ -188,7 +181,7 @@ def test_unresolved_forward_reference_throws():
 def test_list(se, de, opt):
     @serde.serde(**opt)
     class Variant:
-        d: List
+        d: list
 
     # List can contain different types (except Toml).
     if se is not serde.toml.to_toml:
@@ -201,10 +194,10 @@ def test_list(se, de, opt):
 def test_dict_with_non_str_keys(se, de, opt):
     @serde.serde(**opt)
     class Foo:
-        i: Dict[int, int]
-        s: Dict[str, str]
-        f: Dict[float, float]
-        b: Dict[bool, bool]
+        i: dict[int, int]
+        s: dict[str, str]
+        f: dict[float, float]
+        b: dict[bool, bool]
 
     if se not in (serde.json.to_json, serde.msgpack.to_msgpack, serde.toml.to_toml):
         # JSON, Msgpack, Toml don't allow non string key.
@@ -275,14 +268,13 @@ def test_enum(se, de, opt):
 @pytest.mark.parametrize("se,de", all_formats)
 def test_tuple(se, de, opt):
     @serde.serde(**opt)
-    @dataclasses.dataclass
     class Homogeneous:
-        i: Tuple[int, int]
-        s: Tuple[str, str]
-        f: Tuple[float, float]
-        b: Tuple[bool, bool]
+        i: tuple[int, int]
+        s: tuple[str, str]
+        f: tuple[float, float]
+        b: tuple[bool, bool]
 
-    def uncheck_new(i: List[Any], s: List[Any], f: List[Any], b: List[Any]) -> Homogeneous:
+    def uncheck_new(i: list[Any], s: list[Any], f: list[Any], b: list[Any]) -> Homogeneous:
         """
         Bypass runtime type checker by mutating inner value.
         """
@@ -301,9 +293,8 @@ def test_tuple(se, de, opt):
     assert tuple_but_actually_list != de(Homogeneous, se(tuple_but_actually_list))
 
     @serde.serde(**opt)
-    @dataclasses.dataclass
     class Variant:
-        t: Tuple[int, str, float, bool]
+        t: tuple[int, str, float, bool]
 
     # Toml doesn't support variant type of array.
     if se is not serde.toml.to_toml:
@@ -311,23 +302,21 @@ def test_tuple(se, de, opt):
         assert b == de(Variant, se(b))
 
     @serde.serde(**opt)
-    @dataclasses.dataclass
-    class BareTuple:
-        t: Tuple  # type: ignore
+    class Baretuple:
+        t: tuple  # type: ignore
 
-    c = BareTuple((10, 20))
-    assert c == de(BareTuple, se(c))
+    c = Baretuple((10, 20))
+    assert c == de(Baretuple, se(c))
 
-    c = BareTuple(())
-    assert c == de(BareTuple, se(c))
+    c = Baretuple(())
+    assert c == de(Baretuple, se(c))
 
     @serde.serde(**opt)
-    @dataclasses.dataclass
     class Nested:
-        i: Tuple[data.Int, data.Int]
-        s: Tuple[data.Str, data.Str]
-        f: Tuple[data.Float, data.Float]
-        b: Tuple[data.Bool, data.Bool]
+        i: tuple[data.Int, data.Int]
+        s: tuple[data.Str, data.Str]
+        f: tuple[data.Float, data.Float]
+        b: tuple[data.Bool, data.Bool]
 
     d = Nested(
         (data.Int(10), data.Int(20)),
@@ -338,32 +327,30 @@ def test_tuple(se, de, opt):
     assert d == de(Nested, se(d))
 
     @serde.serde(**opt)
-    @dataclasses.dataclass
-    class VariableTuple:
-        t: Tuple[int, int, int]
-        i: Tuple[data.Inner, data.Inner]
+    class Variabletuple:
+        t: tuple[int, int, int]
+        i: tuple[data.Inner, data.Inner]
 
-    e = VariableTuple((1, 2, 3), (data.Inner(0), data.Inner(1)))
-    assert e == de(VariableTuple, se(e))
+    e = Variabletuple((1, 2, 3), (data.Inner(0), data.Inner(1)))
+    assert e == de(Variabletuple, se(e))
 
     with pytest.raises((serde.SerdeError, BeartypeCallHintViolation)):
-        e = VariableTuple((), ())
-        assert e == de(VariableTuple, se(e))
+        e = Variabletuple((), ())
+        assert e == de(Variabletuple, se(e))
 
     with pytest.raises((serde.SerdeError, SyntaxError)):
 
         @serde.serde(**opt)
-        @dataclasses.dataclass
-        class EmptyTuple:
-            t: Tuple[()]
+        class Emptytuple:
+            t: tuple[()]
 
 
 @pytest.mark.parametrize("se,de", all_formats)
 def test_single_element_tuples(se, de):
     @serde.serde
     class Foo:
-        a: Tuple[int]
-        b: Tuple[uuid.UUID]
+        a: tuple[int]
+        b: tuple[uuid.UUID]
 
     foo = Foo(a=(1,), b=(uuid.UUID("855f07da-c3cd-46f2-9557-b8dbeb99ff42"),))
     assert serde.to_dict(foo) == {"a": foo.a, "b": foo.b}
@@ -376,7 +363,7 @@ def test_dataclass_default_factory(se, de):
     @serde.serde
     class Foo:
         foo: str
-        items: Dict[str, int] = serde.field(default_factory=dict)
+        items: dict[str, int] = serde.field(default_factory=dict)
 
     f = Foo("bar")
     assert f == de(Foo, se(f))
@@ -473,7 +460,6 @@ def test_msgpack_unnamed():
 
 def test_toml():
     @serde.serde
-    @dataclasses.dataclass
     class Foo:
         v: Optional[int]
 
@@ -487,9 +473,8 @@ def test_toml():
         serde.toml.to_toml(f)
 
     @serde.serde
-    @dataclasses.dataclass
     class Foo:
-        v: Set[int]
+        v: set[int]
 
     f = Foo({1, 2, 3})
     serde.toml.to_toml(f)
@@ -640,10 +625,10 @@ def test_default_rename_and_alias():
 def test_skip_if(se, de):
     @serde.serde
     class Foo:
-        comments: Optional[List[str]] = serde.field(
+        comments: Optional[list[str]] = serde.field(
             default_factory=list, skip_if=lambda v: len(v) == 0
         )
-        attrs: Optional[Dict[str, str]] = serde.field(
+        attrs: Optional[dict[str, str]] = serde.field(
             default_factory=dict, skip_if=lambda v: v is None or len(v) == 0
         )
 
@@ -660,7 +645,7 @@ def test_skip_if(se, de):
 def test_skip_if_false(se, de):
     @serde.serde
     class Foo:
-        comments: Optional[List[str]] = serde.field(default_factory=list, skip_if_false=True)
+        comments: Optional[list[str]] = serde.field(default_factory=list, skip_if_false=True)
 
     f = Foo(["foo"])
     assert f == de(Foo, se(f))
@@ -673,7 +658,7 @@ def test_skip_if_false(se, de):
 def test_skip_if_overrides_skip_if_false(se, de):
     @serde.serde
     class Foo:
-        comments: Optional[List[str]] = serde.field(
+        comments: Optional[list[str]] = serde.field(
             default_factory=list, skip_if_false=True, skip_if=lambda v: len(v) == 1
         )
 
@@ -699,7 +684,6 @@ def test_skip_if_default(se, de):
 
 @pytest.mark.parametrize("se,de", format_msgpack)
 def test_inheritance(se, de):
-    from dataclasses import dataclass
 
     @serde.serde
     class Base:
@@ -707,7 +691,6 @@ def test_inheritance(se, de):
         b: str
 
     @serde.serde
-    @dataclass
     class Derived(Base):
         c: float
 
@@ -897,7 +880,6 @@ def test_user_error():
         pass
 
     @serde.serde
-    @dataclasses.dataclass
     class Foo:
         v: int
 
@@ -914,9 +896,8 @@ def test_user_error():
 
 def test_frozenset() -> None:
     @serde.serde
-    @dataclasses.dataclass
     class Foo:
-        d: FrozenSet[int]
+        d: frozenset[int]
 
     f = Foo(frozenset({1, 1, 2}))
     assert '{"d":[1,2]}' == serde.json.to_json(f)
@@ -926,7 +907,7 @@ def test_frozenset() -> None:
     assert isinstance(f.d, frozenset)
     assert isinstance(ff.d, frozenset)
 
-    fs = serde.json.from_json(FrozenSet[int], "[1,2]")
+    fs = serde.json.from_json(frozenset[int], "[1,2]")
     assert fs == frozenset([1, 2])
 
 
@@ -934,9 +915,8 @@ def test_defaultdict() -> None:
     from collections import defaultdict
 
     @serde.serde
-    @dataclasses.dataclass
     class Foo:
-        v: DefaultDict[str, List[int]]
+        v: defaultdict[str, list[int]]
 
     f = Foo(defaultdict(list, {"k": [1, 2]}))
     assert '{"v":{"k":[1,2]}}' == serde.json.to_json(f)
@@ -946,22 +926,21 @@ def test_defaultdict() -> None:
     assert isinstance(f.v, defaultdict)
     assert isinstance(ff.v, defaultdict)
 
-    dd = serde.json.from_json(DefaultDict[str, List[int]], '{"k":[1,2]}')
+    dd = serde.json.from_json(defaultdict[str, list[int]], '{"k":[1,2]}')
     assert isinstance(dd, defaultdict)
     assert dd == defaultdict(list, {"k": [1, 2]})
 
 
 def test_defaultdict_invalid_value_type() -> None:
     with pytest.raises(Exception):
-        serde.json.from_json(DefaultDict[str, ...], '{"k":[1,2]}')
+        serde.json.from_json(defaultdict[str, ...], '{"k":[1,2]}')
 
     with pytest.raises(Exception):
-        serde.json.from_json(DefaultDict, '{"k":[1,2]}')
+        serde.json.from_json(defaultdict, '{"k":[1,2]}')
 
 
 def test_class_var() -> None:
     @serde.serde
-    @dataclasses.dataclass
     class Disabled:
         v: ClassVar[int] = 10
 
@@ -970,7 +949,6 @@ def test_class_var() -> None:
     assert a == serde.from_dict(Disabled, {})
 
     @serde.serde(serialize_class_var=True)
-    @dataclasses.dataclass
     class Enabled:
         v: ClassVar[int] = 10
 
@@ -979,12 +957,10 @@ def test_class_var() -> None:
     assert b == serde.from_dict(Enabled, {})
 
     @serde.serde
-    @dataclasses.dataclass
     class Foo:
         v: int
 
     @serde.serde(serialize_class_var=True)
-    @dataclasses.dataclass
     class Dataclass:
         v: ClassVar[Foo] = Foo(10)
 
@@ -993,9 +969,8 @@ def test_class_var() -> None:
     assert c == serde.from_dict(Dataclass, {})
 
     @serde.serde(serialize_class_var=True)
-    @dataclasses.dataclass
     class ClassList:
-        v: ClassVar[List[Foo]] = [Foo(10), Foo(20)]
+        v: ClassVar[list[Foo]] = [Foo(10), Foo(20)]
 
     d = ClassList()
     assert {"v": [{"v": 10}, {"v": 20}]} == serde.to_dict(d)
@@ -1008,12 +983,10 @@ def test_class_var() -> None:
     assert e == serde.from_dict(Enabled, {})
 
     @serde.serde(serialize_class_var=True)
-    @dataclasses.dataclass
     class Bar:
         v: ClassVar[int] = 100
 
     @serde.serde(serialize_class_var=True)
-    @dataclasses.dataclass
     class Nested:
         v: ClassVar[Bar] = Bar()
 
@@ -1024,7 +997,6 @@ def test_class_var() -> None:
 
 def test_dataclass_without_serde() -> None:
     @serde.serde(rename_all="kebabcase")
-    @dataclasses.dataclass
     class Foo:
         myLongName: int
 
@@ -1040,12 +1012,10 @@ def test_dataclass_without_serde() -> None:
 
 def test_dataclass_add_serialize() -> None:
     @serde.serde(rename_all="kebabcase")
-    @dataclasses.dataclass
     class Foo:
         myLongName: int
 
     @serde.deserialize
-    @dataclasses.dataclass
     class Wrapper:
         foo: Foo
 
@@ -1057,12 +1027,10 @@ def test_dataclass_add_serialize() -> None:
 
 def test_dataclass_add_deserialize() -> None:
     @serde.serde(rename_all="kebabcase")
-    @dataclasses.dataclass
     class Foo:
         myLongName: int
 
     @serde.serialize
-    @dataclasses.dataclass
     class Wrapper:
         foo: Foo
 
@@ -1078,7 +1046,6 @@ def test_nested_dataclass_without_serde() -> None:
         v: int
 
     @serde.serde
-    @dataclasses.dataclass
     class Wrapper:
         foo: Foo
 
@@ -1090,12 +1057,10 @@ def test_nested_dataclass_without_serde() -> None:
 
 def test_nested_dataclass_add_deserialize() -> None:
     @serde.serialize
-    @dataclasses.dataclass
     class Foo:
         v: int
 
     @serde.serde
-    @dataclasses.dataclass
     class Wrapper:
         foo: Foo
 
@@ -1107,12 +1072,10 @@ def test_nested_dataclass_add_deserialize() -> None:
 
 def test_nested_dataclass_add_serialize() -> None:
     @serde.deserialize
-    @dataclasses.dataclass
     class Foo:
         v: int
 
     @serde.serde
-    @dataclasses.dataclass
     class Wrapper:
         foo: Foo
 
@@ -1128,7 +1091,6 @@ def test_nested_dataclass_ignore_wrapper_options() -> None:
         myLongName: int
 
     @serde.serde(rename_all="kebabcase")
-    @dataclasses.dataclass
     class Wrapper:
         foo: Foo
 
@@ -1140,7 +1102,6 @@ def test_nested_dataclass_ignore_wrapper_options() -> None:
 
 def test_deserialize_from_incompatible_value() -> None:
     @serde.deserialize
-    @dataclasses.dataclass
     class Foo:
         v: int
 
diff --git a/tests/test_compat.py b/tests/test_compat.py
index 120fe58..b48565c 100644
--- a/tests/test_compat.py
+++ b/tests/test_compat.py
@@ -2,7 +2,6 @@ import sys
 from dataclasses import dataclass
 from datetime import datetime
 from typing import Any, Generic, NewType, Optional, TypeVar, Union, Literal
-from beartype.typing import Dict, List, Set, Tuple
 
 import pytest
 
@@ -32,15 +31,15 @@ U = TypeVar("U")
 
 
 def test_types() -> None:
-    assert is_list(List[int])
-    assert is_list(List)
-    assert is_set(Set[int])
-    assert is_set(Set)
-    assert is_tuple(Tuple[int, int, int])
-    assert is_tuple(Tuple)
-    assert is_tuple(Tuple[int, ...])
-    assert is_dict(Dict[str, int])
-    assert is_dict(Dict)
+    assert is_list(list[int])
+    assert is_list(list)
+    assert is_set(set[int])
+    assert is_set(set)
+    assert is_tuple(tuple[int, int, int])
+    assert is_tuple(tuple)
+    assert is_tuple(tuple[int, ...])
+    assert is_dict(dict[str, int])
+    assert is_dict(dict)
     assert is_opt(Optional[int])
     assert is_opt(Union[int, None])
     assert is_union(Union[int, str])
@@ -80,27 +79,27 @@ def test_typename() -> None:
 
     assert typename(Foo[int]) == "Foo"
     assert typename(Foo) == "Foo"
-    assert typename(List[int]) == "List[int]"
+    assert typename(list[int]) == "list[int]"
     assert typename(Optional) == "Optional"
-    assert typename(List) == "List"
-    assert typename(List[int]) == "List[int]"
-    assert typename(Tuple) == "Tuple"
-    assert typename(Tuple[int, str]) == "Tuple[int, str]"
-    assert typename(Tuple[int, ...]) == "Tuple[int, ...]"
-    assert typename(Dict) == "Dict"
-    assert typename(Dict[str, Foo]) == "Dict[str, Foo]"  # type: ignore
-    assert typename(Set) == "Set"
-    assert typename(Set[int]) == "Set[int]"
+    assert typename(list) == "list"
+    assert typename(list[int]) == "list[int]"
+    assert typename(tuple) == "tuple"
+    assert typename(tuple[int, str]) == "tuple[int, str]"
+    assert typename(tuple[int, ...]) == "tuple[int, ...]"
+    assert typename(dict) == "dict"
+    assert typename(dict[str, Foo]) == "dict[str, Foo]"  # type: ignore
+    assert typename(set) == "set"
+    assert typename(set[int]) == "set[int]"
     assert typename(Literal[1, True, "Hey"]) == "Literal[1, True, Hey]"
 
 
 def test_iter_types() -> None:
     assert {Pri, int, str, float, bool} == set(iter_types(Pri))
-    assert {Dict, str, Pri, int, float, bool} == set(iter_types(Dict[str, Pri]))
-    assert {List, str} == set(iter_types(List[str]))
-    assert {Set, str} == set(iter_types(Set[str]))
-    assert {Tuple, int, str, bool, float} == set(iter_types(Tuple[int, str, bool, float]))
-    assert {Tuple, int, Ellipsis} == set(iter_types(Tuple[int, ...]))
+    assert {dict, str, Pri, int, float, bool} == set(iter_types(dict[str, Pri]))
+    assert {list, str} == set(iter_types(list[str]))
+    assert {set, str} == set(iter_types(set[str]))
+    assert {tuple, int, str, bool, float} == set(iter_types(tuple[int, str, bool, float]))
+    assert {tuple, int, Ellipsis} == set(iter_types(tuple[int, ...]))
     assert {PriOpt, Optional, int, str, float, bool} == set(iter_types(PriOpt))
 
     @serde.serde
@@ -110,25 +109,25 @@ def test_iter_types() -> None:
         c: datetime
         d: Optional[str] = None
         e: Union[str, int] = 10
-        f: List[int] = serde.field(default_factory=list)
-        g: Set[int] = serde.field(default_factory=set)
+        f: list[int] = serde.field(default_factory=list)
+        g: set[int] = serde.field(default_factory=set)
 
-    assert {Foo, datetime, Optional, str, Union, List, Set, int} == set(iter_types(Foo))
+    assert {Foo, datetime, Optional, str, Union, list, set, int} == set(iter_types(Foo))
 
 
 def test_iter_unions() -> None:
     assert [Union[str, int]] == list(iter_unions(Union[str, int]))
-    assert [Union[str, int]] == list(iter_unions(Dict[str, Union[str, int]]))
-    assert [Union[str, int]] == list(iter_unions(Tuple[Union[str, int]]))
-    assert [Union[str, int]] == list(iter_unions(List[Union[str, int]]))
-    assert [Union[str, int]] == list(iter_unions(Set[Union[str, int]]))
+    assert [Union[str, int]] == list(iter_unions(dict[str, Union[str, int]]))
+    assert [Union[str, int]] == list(iter_unions(tuple[Union[str, int]]))
+    assert [Union[str, int]] == list(iter_unions(list[Union[str, int]]))
+    assert [Union[str, int]] == list(iter_unions(set[Union[str, int]]))
     assert [Union[str, int, type(None)]] == list(iter_unions(Optional[Union[str, int]]))
 
     @dataclass
     class A:
-        a: List[Union[int, str]]
-        b: Dict[str, List[Union[float, int]]]
-        C: Dict[Union[bool, str], Union[float, int]]
+        a: list[Union[int, str]]
+        b: dict[str, list[Union[float, int]]]
+        C: dict[Union[bool, str], Union[float, int]]
 
     assert {Union[int, str], Union[float, int], Union[bool, str], Union[float, int]} == set(
         iter_unions(A)
@@ -136,14 +135,14 @@ def test_iter_unions() -> None:
 
 
 def test_type_args() -> None:
-    assert (int,) == type_args(List[int])
-    assert (int, str) == type_args(Dict[int, str])
+    assert (int,) == type_args(list[int])
+    assert (int, str) == type_args(dict[int, str])
     assert (int, type(None)) == type_args(Optional[int])
-    assert (Optional[int],) == type_args(List[Optional[int]])
-    assert (List[int], type(None)) == type_args(Optional[List[int]])
-    assert (List[int], Dict[str, int]) == type_args(Union[List[int], Dict[str, int]])
+    assert (Optional[int],) == type_args(list[Optional[int]])
+    assert (list[int], type(None)) == type_args(Optional[list[int]])
+    assert (list[int], dict[str, int]) == type_args(Union[list[int], dict[str, int]])
     assert (int, type(None), str) == type_args(Union[Optional[int], str])
-    assert (int, Ellipsis) == type_args(Tuple[int, ...])
+    assert (int, Ellipsis) == type_args(tuple[int, ...])
 
     if sys.version_info[:3] >= (3, 9, 0):
         assert (int,) == type_args(list[int])
@@ -154,7 +153,7 @@ def test_type_args() -> None:
 
 def test_union_args() -> None:
     assert (int, str) == union_args(Union[int, str])
-    assert (List[int], Dict[int, str]) == union_args(Union[List[int], Dict[int, str]])
+    assert (list[int], dict[int, str]) == union_args(Union[list[int], dict[int, str]])
     assert (Optional[int], str) == union_args(Union[Optional[int], str])
 
 
@@ -184,57 +183,56 @@ def test_is_instance() -> None:
     h.p.i = 10.0  # type: ignore
     assert is_instance(h, Foo)
 
-    # List
-    assert is_instance([], List[int])
-    assert is_instance([10], List)
+    # list
+    assert is_instance([], list[int])
     assert is_instance([10], list)
-    assert is_instance([10], List[int])
-    assert not is_instance([10.0], List[int])
+    assert is_instance([10], list)
+    assert is_instance([10], list[int])
+    assert not is_instance([10.0], list[int])
 
-    # List of dataclasses
-    assert is_instance([Int(n) for n in range(1, 10)], List[Int])
-    assert not is_instance([Str("foo")], List[Int])
+    # list of dataclasses
+    assert is_instance([Int(n) for n in range(1, 10)], list[Int])
+    assert not is_instance([Str("foo")], list[Int])
 
-    # Set
-    assert is_instance(set(), Set[int])
-    assert is_instance({10}, Set)
+    # set
+    assert is_instance(set(), set[int])
     assert is_instance({10}, set)
-    assert is_instance({10}, Set[int])
-    assert not is_instance({10.0}, Set[int])
+    assert is_instance({10}, set[int])
+    assert not is_instance({10.0}, set[int])
 
-    # Set of dataclasses
-    assert is_instance({Int(n) for n in range(1, 10)}, Set[Int])
-    assert not is_instance({Str("foo")}, Set[Int])
+    # set of dataclasses
+    assert is_instance({Int(n) for n in range(1, 10)}, set[Int])
+    assert not is_instance({Str("foo")}, set[Int])
 
-    # Tuple
-    assert is_instance((), Tuple[int, str, float, bool])
-    assert is_instance((10, "a"), Tuple)
+    # tuple
+    assert is_instance((), tuple[int, str, float, bool])
+    assert is_instance((10, "a"), tuple)
     assert is_instance((10, "a"), tuple)
-    assert is_instance((10, "foo", 100.0, True), Tuple[int, str, float, bool])
-    assert not is_instance((10, "foo", 100.0, "last-type-is-wrong"), Tuple[int, str, float, bool])
-    assert is_instance((10, 20), Tuple[int, ...])
-    assert is_instance((10, 20, 30), Tuple[int, ...])
-    assert is_instance((), Tuple[int, ...])
-    assert not is_instance((10, "a"), Tuple[int, ...])
-
-    # Tuple of dataclasses
+    assert is_instance((10, "foo", 100.0, True), tuple[int, str, float, bool])
+    assert not is_instance((10, "foo", 100.0, "last-type-is-wrong"), tuple[int, str, float, bool])
+    assert is_instance((10, 20), tuple[int, ...])
+    assert is_instance((10, 20, 30), tuple[int, ...])
+    assert is_instance((), tuple[int, ...])
+    assert not is_instance((10, "a"), tuple[int, ...])
+
+    # tuple of dataclasses
     assert is_instance(
-        (Int(10), Str("foo"), Float(100.0), Bool(True)), Tuple[Int, Str, Float, Bool]
+        (Int(10), Str("foo"), Float(100.0), Bool(True)), tuple[Int, Str, Float, Bool]
     )
     assert not is_instance(
-        (Int(10), Str("foo"), Str("wrong-class"), Bool(True)), Tuple[Int, Str, Float, Bool]
+        (Int(10), Str("foo"), Str("wrong-class"), Bool(True)), tuple[Int, Str, Float, Bool]
     )
 
-    # Dict
-    assert is_instance({}, Dict[str, int])
-    assert is_instance({"a": "b"}, Dict)
+    # dict
+    assert is_instance({}, dict[str, int])
+    assert is_instance({"a": "b"}, dict)
     assert is_instance({"a": "b"}, dict)
-    assert is_instance({"foo": 10, "bar": 20}, Dict[str, int])
-    assert not is_instance({"foo": 10.0, "bar": 20}, Dict[str, int])
+    assert is_instance({"foo": 10, "bar": 20}, dict[str, int])
+    assert not is_instance({"foo": 10.0, "bar": 20}, dict[str, int])
 
-    # Dict of dataclasses
-    assert is_instance({Str("foo"): Int(10), Str("bar"): Int(20)}, Dict[Str, Int])
-    assert not is_instance({Str("foo"): Str("wrong-type"), Str("bar"): Int(10)}, Dict[Str, Int])
+    # dict of dataclasses
+    assert is_instance({Str("foo"): Int(10), Str("bar"): Int(20)}, dict[Str, Int])
+    assert not is_instance({Str("foo"): Str("wrong-type"), Str("bar"): Int(10)}, dict[Str, Int])
 
     # Optional
     assert is_instance(None, type(None))
@@ -247,9 +245,9 @@ def test_is_instance() -> None:
     assert not is_instance("wrong-type", Optional[Int])
 
     # Nested containers
-    assert is_instance([({"a": "b"}, 10, [True])], List[Tuple[Dict[str, str], int, List[bool]]])
+    assert is_instance([({"a": "b"}, 10, [True])], list[tuple[dict[str, str], int, list[bool]]])
     assert not is_instance(
-        [({"a": "b"}, 10, ["wrong-type"])], List[Tuple[Dict[str, str], int, List[bool]]]
+        [({"a": "b"}, 10, ["wrong-type"])], list[tuple[dict[str, str], int, list[bool]]]
     )
 
 
@@ -260,17 +258,17 @@ class GenericFoo(Generic[T]):
 
 def test_is_generic() -> None:
     assert not is_generic(int)
-    assert not is_generic(List[int])
-    assert not is_generic(List)
+    assert not is_generic(list[int])
+    assert not is_generic(list)
     assert not is_generic(GenericFoo)
     assert is_generic(GenericFoo[int])
-    assert is_generic(GenericFoo[List[int]])
+    assert is_generic(GenericFoo[list[int]])
     assert not is_generic(Optional[int])
-    assert not is_generic(Optional[List[int]])
+    assert not is_generic(Optional[list[int]])
     assert serde.is_serializable(GenericFoo)
-    assert not serde.is_serializable(GenericFoo[List[int]])
+    assert not serde.is_serializable(GenericFoo[list[int]])
     assert serde.is_deserializable(GenericFoo)
-    assert not serde.is_deserializable(GenericFoo[List[int]])
+    assert not serde.is_deserializable(GenericFoo[list[int]])
 
 
 def test_get_generic_arg() -> None:
diff --git a/tests/test_custom.py b/tests/test_custom.py
index 648d7b9..3b44637 100644
--- a/tests/test_custom.py
+++ b/tests/test_custom.py
@@ -3,8 +3,7 @@ Tests for custom serializer/deserializer.
 """
 
 from datetime import datetime
-from typing import Optional, Any, Type, Union
-from beartype.typing import List, Dict
+from typing import Optional, Any, Union
 from plum import dispatch
 
 import pytest
@@ -81,19 +80,19 @@ def test_custom_class_serializer() -> None:
 
     class MyDeserializer(ClassDeserializer):
         @dispatch
-        def deserialize(self, cls: Type[datetime], value: Any) -> datetime:
+        def deserialize(self, cls: type[datetime], value: Any) -> datetime:
             return datetime.strptime(value, "%d/%m/%y")
 
     @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
     class Bar:
         e: datetime
-        f: List[datetime]
+        f: list[datetime]
 
     @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
     class Foo:
         a: datetime
-        b: List[datetime]
-        c: Dict[str, datetime]
+        b: list[datetime]
+        c: dict[str, datetime]
         d: Bar
 
     dt = datetime(2021, 1, 1, 0, 0, 0)
@@ -123,7 +122,7 @@ def test_custom_class_serializer_dataclass() -> None:
 
     class MyDeserializer(ClassDeserializer):
         @dispatch
-        def deserialize(self, cls: Type[Bar], value: Any) -> Bar:
+        def deserialize(self, cls: type[Bar], value: Any) -> Bar:
             return Bar(int(value))
 
     @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
@@ -145,7 +144,7 @@ def test_custom_serializer_with_field_attributes() -> None:
 
     class MyDeserializer(ClassDeserializer):
         @dispatch
-        def deserialize(self, cls: Type[datetime], value: Any) -> datetime:
+        def deserialize(self, cls: type[datetime], value: Any) -> datetime:
             return datetime.strptime(value, "%d/%m/%y")
 
     @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
@@ -174,7 +173,7 @@ def test_custom_serializer_with_class_attributes() -> None:
 
     class MyDeserializer(ClassDeserializer):
         @dispatch
-        def deserialize(self, cls: Type[datetime], value: Any) -> datetime:
+        def deserialize(self, cls: type[datetime], value: Any) -> datetime:
             return datetime.strptime(value, "%d/%m/%y")
 
     @serde(
@@ -204,7 +203,7 @@ def test_field_serialize_override_class_serializer() -> None:
 
     class MyDeserializer(ClassDeserializer):
         @dispatch
-        def deserialize(self, cls: Type[datetime], value: Any) -> datetime:
+        def deserialize(self, cls: type[datetime], value: Any) -> datetime:
             return datetime.strptime(value, "%d/%m/%y")
 
     @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
@@ -236,7 +235,7 @@ def test_custom_serializer_union() -> None:
 
     class MyDeserializer(ClassDeserializer):
         @dispatch
-        def deserialize(self, cls: Type[datetime], value: Any) -> datetime:
+        def deserialize(self, cls: type[datetime], value: Any) -> datetime:
             return datetime.strptime(value, "%d/%m/%y")
 
     @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
@@ -266,7 +265,7 @@ def test_custom_class_serializer_optional() -> None:
 
     class MyDeserializer(ClassDeserializer):
         @dispatch
-        def deserialize(self, cls: Type[datetime], value: Any) -> datetime:
+        def deserialize(self, cls: type[datetime], value: Any) -> datetime:
             return datetime.strptime(value, "%d/%m/%y")
 
     @serde(class_serializer=MySerializer(), class_deserializer=MyDeserializer())
diff --git a/tests/test_de.py b/tests/test_de.py
index 0092704..9374ba3 100644
--- a/tests/test_de.py
+++ b/tests/test_de.py
@@ -1,7 +1,5 @@
 from decimal import Decimal
 from typing import Union
-from beartype.typing import List, Tuple
-
 from serde.de import from_obj
 
 
@@ -12,16 +10,16 @@ def test_from_obj() -> None:
     # Union
     assert "a" == from_obj(Union[int, str], "a", False, False)
 
-    # Tuple
-    assert ("a", "b") == from_obj(Tuple[str, str], ("a", "b"), False, False)
+    # tuple
+    assert ("a", "b") == from_obj(tuple[str, str], ("a", "b"), False, False)
 
     # pyserde converts to the specified type
     assert 10 == from_obj(int, "10", False, False)
 
-    # pyserde can converts for container types e.g. Tuple, List etc.
-    assert (1, 2) == from_obj(Tuple[int, int], ("1", "2"), False, False)
+    # pyserde can converts for container types e.g. tuple, List etc.
+    assert (1, 2) == from_obj(tuple[int, int], ("1", "2"), False, False)
 
     # Decimal
-    dec = from_obj(List[Decimal], ("0.1", 0.1), False, False)
+    dec = from_obj(list[Decimal], ("0.1", 0.1), False, False)
     assert isinstance(dec[0], Decimal) and dec[0] == Decimal("0.1")
     assert isinstance(dec[1], Decimal) and dec[1] == Decimal(0.1)
diff --git a/tests/test_flatten.py b/tests/test_flatten.py
index 095a774..b0583f9 100644
--- a/tests/test_flatten.py
+++ b/tests/test_flatten.py
@@ -3,7 +3,6 @@ Tests for flatten attribute.
 """
 
 from typing import Any
-from beartype.typing import Dict, List
 
 import pytest
 
@@ -35,8 +34,8 @@ def test_flatten_simple() -> None:
 def test_flatten(se: Any, de: Any) -> None:
     @serde
     class Baz:
-        e: List[int]
-        f: Dict[str, str]
+        e: list[int]
+        f: dict[str, str]
 
     @serde
     class Bar:
diff --git a/tests/test_lazy_type_evaluation.py b/tests/test_lazy_type_evaluation.py
index d4dc6a2..3a2f784 100644
--- a/tests/test_lazy_type_evaluation.py
+++ b/tests/test_lazy_type_evaluation.py
@@ -2,7 +2,6 @@ from __future__ import annotations  # this is the line this test file is all abo
 
 import dataclasses
 from enum import Enum
-from beartype.typing import List, Tuple
 
 import pytest
 
@@ -19,13 +18,13 @@ class Status(Enum):
 class A:
     a: int
     b: Status
-    c: List[str]
+    c: list[str]
 
 
 @serde
 class B:
     a: A
-    b: Tuple[str, A]
+    b: tuple[str, A]
     c: Status
 
 
diff --git a/tests/test_legacy_custom.py b/tests/test_legacy_custom.py
index 0b1838e..2c25238 100644
--- a/tests/test_legacy_custom.py
+++ b/tests/test_legacy_custom.py
@@ -4,7 +4,6 @@ Tests for custom serializer/deserializer.
 
 from datetime import datetime
 from typing import Optional, Union
-from beartype.typing import List, Set
 
 import pytest
 
@@ -40,8 +39,8 @@ def test_legacy_custom_class_serializer():
         c: datetime
         d: Optional[str] = None
         e: Union[str, int] = 10
-        f: List[int] = field(default_factory=list)
-        g: Set[int] = field(default_factory=set)
+        f: list[int] = field(default_factory=list)
+        g: set[int] = field(default_factory=set)
 
     dt = datetime(2021, 1, 1, 0, 0, 0)
     f = Foo(10, dt, dt, f=[1, 2, 3], g={4, 5, 6})
diff --git a/tests/test_literal.py b/tests/test_literal.py
index 8bb542c..1b13201 100644
--- a/tests/test_literal.py
+++ b/tests/test_literal.py
@@ -1,7 +1,6 @@
 import logging
 from dataclasses import dataclass
 from typing import Literal
-from beartype.typing import Dict, List, Tuple
 
 import pytest
 
@@ -70,10 +69,10 @@ class LitDict:
     Dict containing primitives.
     """
 
-    i: Dict[Literal[1, 2], Literal[1, 2]]
-    s: Dict[Literal["foo", "bar"], Literal["foo", "bar"]]
-    b: Dict[Literal[True], Literal[True]]
-    m: Dict[
+    i: dict[Literal[1, 2], Literal[1, 2]]
+    s: dict[Literal["foo", "bar"], Literal["foo", "bar"]]
+    b: dict[Literal[True], Literal[True]]
+    m: dict[
         Literal[1, 2, "foo", "bar", False, True],
         Literal[1, 2, "foo", "bar", False, True],
     ]
@@ -86,10 +85,10 @@ class LitStrDict:
     Dict containing primitives.
     """
 
-    i: Dict[Literal["1", "2"], Literal[1, 2]]
-    s: Dict[Literal["foo", "bar"], Literal["foo", "bar"]]
-    b: Dict[Literal["True"], Literal[True]]
-    m: Dict[
+    i: dict[Literal["1", "2"], Literal[1, 2]]
+    s: dict[Literal["foo", "bar"], Literal["foo", "bar"]]
+    b: dict[Literal["True"], Literal[True]]
+    m: dict[
         Literal["1", "2", "foo", "bar", "False", "True"],
         Literal[1, 2, "foo", "bar", False, True],
     ]
@@ -109,20 +108,20 @@ class Literals:
 
 
 @serde.serde
-class LitNestedPriTuple:
+class LitNestedPrituple:
     """
-    Tuple containing nested primitives.
+    tuple containing nested primitives.
     """
 
-    i: Tuple[LitInt, LitInt]
-    s: Tuple[LitStr, LitStr]
-    b: Tuple[LitBool, LitBool, LitBool]
-    m: Tuple[LitMixed, LitMixed]
+    i: tuple[LitInt, LitInt]
+    s: tuple[LitStr, LitStr]
+    b: tuple[LitBool, LitBool, LitBool]
+    m: tuple[LitMixed, LitMixed]
 
 
-ListLiterals = List[Literals]
+listLiterals = list[Literals]
 PRI = Literals(1, "foo", True, 2)
-DictLiterals = Dict[str, Literals]
+DictLiterals = dict[str, Literals]
 
 
 """
@@ -146,13 +145,13 @@ def test_dict(se, de, opt):
 @pytest.mark.parametrize("se,de", all_formats)
 def test_tuple(se, de, opt):
     if se is not serde.toml.to_toml:
-        p = LitNestedPriTuple(
+        p = LitNestedPrituple(
             (LitInt(1), LitInt(2)),
             (LitStr("foo"), LitStr("bar")),
             (LitBool(True), LitBool(True), LitBool(True)),
             (LitMixed(False), LitMixed("foo")),
         )
-        assert p == de(LitNestedPriTuple, se(p))
+        assert p == de(LitNestedPrituple, se(p))
 
 
 @pytest.mark.parametrize(
@@ -160,10 +159,10 @@ def test_tuple(se, de, opt):
 )
 def test_list_literals(se, de):
     p = [PRI, PRI]
-    assert p == de(ListLiterals, se(p))
+    assert p == de(listLiterals, se(p))
 
     p = []
-    assert p == de(ListLiterals, se(p))
+    assert p == de(listLiterals, se(p))
 
 
 @pytest.mark.parametrize(
diff --git a/tests/test_numpy.py b/tests/test_numpy.py
index 67ec4c0..7404e75 100644
--- a/tests/test_numpy.py
+++ b/tests/test_numpy.py
@@ -1,6 +1,5 @@
 import logging
 from dataclasses import fields
-from beartype.typing import List
 
 import numpy as np
 import numpy.typing as npt
@@ -109,9 +108,9 @@ def test_encode_numpy(se, de, opt):
         a: int
         b: float
         h: bool
-        c: List[int]
-        d: List[float]
-        e: List[bool]
+        c: list[int]
+        d: list[float]
+        e: list[bool]
 
     def unchecked_new(
         a: np.int32,
@@ -218,9 +217,9 @@ def test_encode_numpy_with_no_default_encoder(se, de):
         a: int
         b: float
         h: bool
-        c: List[int]
-        d: List[float]
-        e: List[bool]
+        c: list[int]
+        d: list[float]
+        e: list[bool]
 
     def unchecked_new(
         a: np.int32,
diff --git a/tests/test_se.py b/tests/test_se.py
index d42e8ac..daa4f99 100644
--- a/tests/test_se.py
+++ b/tests/test_se.py
@@ -1,4 +1,3 @@
-from beartype.typing import Set
 from serde import asdict, astuple, serialize, to_dict, to_tuple
 from serde.json import to_json
 from serde.msgpack import to_msgpack
@@ -72,7 +71,7 @@ def test_se_func_iter():
         )
     )
 
-    # Tuple
+    # tuple
     exp = (
         (10, 10, 10),
         ("10", "10", "10", "10"),
@@ -112,7 +111,7 @@ def test_se_func_iter():
 def test_convert_sets_option():
     @serialize
     class A:
-        v: Set[str]
+        v: set[str]
 
     a = A({"a", "b"})
 
diff --git a/tests/test_type_check.py b/tests/test_type_check.py
index 9f3dd63..91f5d17 100644
--- a/tests/test_type_check.py
+++ b/tests/test_type_check.py
@@ -6,12 +6,6 @@ from typing import (
     Union,
     Any,
 )
-from beartype.typing import (
-    Dict,
-    List,
-    Set,
-    Tuple,
-)
 
 import pytest
 
@@ -20,7 +14,7 @@ import serde.json
 
 from . import data
 
-test_cases: List[Tuple[Any, Any, bool]] = [
+test_cases: list[tuple[Any, Any, bool]] = [
     (int, 10, False),
     (int, 10.0, True),
     (int, "10", True),
@@ -37,33 +31,33 @@ test_cases: List[Tuple[Any, Any, bool]] = [
     (bool, 10.0, True),
     (bool, "10", True),
     (bool, True, False),
-    (List[int], [1], False),
-    (List[int], [1.0], True),
-    (List[float], [1.0], False),
-    (List[float], [1], True),
-    (List[float], ["foo"], True),
-    (List[str], ["foo"], False),
-    (List[str], [True], True),
-    (List[bool], [True], False),
-    (List[bool], [10], True),
-    (List[data.Int], [data.Int(1)], False),
-    (List[data.Int], [data.Int.uncheck_new(1.0)], True),  # Runtime incompatible object
-    (List[data.Int], [], False),
-    (Dict[str, int], {"foo": 10}, False),
-    (Dict[str, int], {"foo": 10.0}, False),
-    (Dict[str, data.Int], {"foo": data.Int(1)}, False),
-    (Dict[str, data.Int], {"foo": data.Int.uncheck_new(1.0)}, True),  # Runtime incompatible object
-    (Set[int], {10}, False),
-    (Set[int], {10.0}, False),
-    (Set[int], [10], True),
-    (Tuple[int], (10,), False),
-    (Tuple[int], (10.0,), True),
-    (Tuple[int, str], (10, "foo"), False),
-    (Tuple[int, str], (10, 10.0), True),
-    (Tuple[data.Int, data.Str], (data.Int(1), data.Str("2")), False),
-    (Tuple[data.Int, data.Str], (data.Int(1), data.Int(2)), True),
-    (Tuple, (10, 10.0), False),
-    (Tuple[int, ...], (1, 2), False),
+    (list[int], [1], False),
+    (list[int], [1.0], True),
+    (list[float], [1.0], False),
+    (list[float], [1], True),
+    (list[float], ["foo"], True),
+    (list[str], ["foo"], False),
+    (list[str], [True], True),
+    (list[bool], [True], False),
+    (list[bool], [10], True),
+    (list[data.Int], [data.Int(1)], False),
+    (list[data.Int], [data.Int.uncheck_new(1.0)], True),  # Runtime incompatible object
+    (list[data.Int], [], False),
+    (dict[str, int], {"foo": 10}, False),
+    (dict[str, int], {"foo": 10.0}, False),
+    (dict[str, data.Int], {"foo": data.Int(1)}, False),
+    (dict[str, data.Int], {"foo": data.Int.uncheck_new(1.0)}, True),  # Runtime incompatible object
+    (set[int], {10}, False),
+    (set[int], {10.0}, False),
+    (set[int], [10], True),
+    (tuple[int], (10,), False),
+    (tuple[int], (10.0,), True),
+    (tuple[int, str], (10, "foo"), False),
+    (tuple[int, str], (10, 10.0), True),
+    (tuple[data.Int, data.Str], (data.Int(1), data.Str("2")), False),
+    (tuple[data.Int, data.Str], (data.Int(1), data.Int(2)), True),
+    (tuple, (10, 10.0), False),
+    (tuple[int, ...], (1, 2), False),
     (data.E, data.E.S, False),
     (data.E, data.IE.V0, True),
     (Union[int, str], 10, False),
@@ -79,11 +73,11 @@ test_cases: List[Tuple[Any, Any, bool]] = [
 # Those test cases have wrong runtime values against declared types.
 # This is not yet testable until beartype implements O(n) type checking
 # https://beartype.readthedocs.io/en/latest/api_decor/#beartype.BeartypeStrategy
-default_unstable_test_cases: List[Tuple[Any, Any, bool]] = [
-    (List[int], [1, 1.0], True),
-    (List[data.Int], [data.Int(1), data.Float(10.0)], True),
-    (Dict[str, int], {"foo": 10, 100: "bar"}, False),
-    (Tuple[int, ...], (1, 2.0), True),
+default_unstable_test_cases: list[tuple[Any, Any, bool]] = [
+    (list[int], [1, 1.0], True),
+    (list[data.Int], [data.Int(1), data.Float(10.0)], True),
+    (dict[str, int], {"foo": 10, 100: "bar"}, False),
+    (tuple[int, ...], (1, 2.0), True),
 ]
 
 
@@ -178,7 +172,7 @@ def test_coerce() -> None:
 
     # Nested structure
     p2 = Nested(Int("10"), Str(100), Float(1000), Bool("True"))  # type: ignore
-    d2: Dict[str, Dict[str, Any]] = serde.to_dict(p2)
+    d2: dict[str, dict[str, Any]] = serde.to_dict(p2)
     assert d2["i"]["i"] == 10
     assert d2["s"]["s"] == "100"
     assert d2["f"]["f"] == 1000.0
diff --git a/tests/test_union.py b/tests/test_union.py
index 6e8d8eb..1db3b0f 100644
--- a/tests/test_union.py
+++ b/tests/test_union.py
@@ -3,12 +3,6 @@ import sys
 import uuid
 from dataclasses import dataclass
 from ipaddress import IPv4Address
-from beartype.typing import (
-    Dict,
-    FrozenSet,
-    List,
-    Tuple,
-)
 from typing import (
     Generic,
     NewType,
@@ -93,7 +87,7 @@ class ContUnion:
     Union Containers.
     """
 
-    v: Union[Dict[str, int], List[int], List[str]]
+    v: Union[dict[str, int], list[int], list[str]]
 
 
 @serde
@@ -172,7 +166,7 @@ def test_union_containers() -> None:
     v = ContUnion({"a": 1, "b": 2, "c": 3})
     s = '{"v":{"a":1,"b":2,"c":3}}'
     assert s == to_json(v)
-    # Note: this only works because Dict[str, int] comes first in Union otherwise a List would win
+    # Note: this only works because dict[str, int] comes first in Union otherwise a list would win
     assert v == from_json(ContUnion, s)
 
 
@@ -292,7 +286,7 @@ def test_optional_complex_type_with_default() -> None:
 def test_union_with_complex_types_in_containers() -> None:
     @serde
     class A:
-        v: Union[List[IPv4Address], List[UUID]]
+        v: Union[list[IPv4Address], list[UUID]]
 
     a_ips = A([IPv4Address("127.0.0.1"), IPv4Address("10.0.0.1")])
     assert a_ips == from_dict(A, to_dict(a_ips, reuse_instances=False), reuse_instances=False)
@@ -385,7 +379,7 @@ def test_union_in_union() -> None:
 def test_union_in_other_type() -> None:
     @serde
     class A:
-        v: Dict[str, Union[UUID, int]]
+        v: dict[str, Union[UUID, int]]
 
     a_uuid = A({"key": UUID("00611ee9-7ca3-41d3-9607-ea7268e264ea")})
     assert a_uuid == from_dict(A, to_dict(a_uuid, reuse_instances=False), reuse_instances=False)
@@ -412,7 +406,7 @@ def test_union_with_list_of_other_class() -> None:
 
     @serde
     class B:
-        b: Union[List[A], str]
+        b: Union[list[A], str]
 
     b = B([A(1)])
     b_dict = {"b": [{"a": 1}]}
@@ -424,7 +418,7 @@ def test_union_with_list_of_other_class() -> None:
 def test_union_with_union_in_nested_types() -> None:
     @serde
     class A:
-        v: Union[UUID, List[Union[UUID, int]]]
+        v: Union[UUID, list[Union[UUID, int]]]
 
     a_uuid = A([UUID("00611ee9-7ca3-41d3-9607-ea7268e264ea")])
     assert to_dict(a_uuid, reuse_instances=False) == {"v": ["00611ee9-7ca3-41d3-9607-ea7268e264ea"]}
@@ -441,7 +435,7 @@ def test_union_with_union_in_nested_types() -> None:
 def test_union_with_union_in_nested_tuple() -> None:
     @serde
     class A:
-        v: Union[bool, Tuple[Union[str, int]]]
+        v: Union[bool, tuple[Union[str, int]]]
 
     a_bool = A(False)
     a_bool_dict = {"v": False}
@@ -514,7 +508,7 @@ def test_external_tagging() -> None:
     class Foo:
         a: Union[Bar, Baz]
         b: Union[int, str]
-        c: Union[Dict[int, str], List[int]]
+        c: Union[dict[int, str], list[int]]
         d: Union[int, Nested]
 
     f = Foo(Bar(10), "foo", {10: "bar"}, Nested(Baz(100)))
@@ -572,7 +566,7 @@ def test_internal_tagging() -> None:
     class Foo:
         a: Union[Bar, Baz]
         b: Union[int, str]
-        c: Union[Dict[int, str], List[int]]
+        c: Union[dict[int, str], list[int]]
         d: Union[int, Nested]
 
     f = Foo(Bar(10), "foo", {10: "bar"}, Nested(Baz(100)))
@@ -628,7 +622,7 @@ def test_adjacent_tagging() -> None:
     class Foo:
         a: Union[Bar, Baz]
         b: Union[int, str]
-        c: Union[Dict[int, str], List[int]]
+        c: Union[dict[int, str], list[int]]
         d: Union[int, Nested]
 
     f = Foo(Bar(10), "foo", {10: "bar"}, Nested(Baz(100)))
@@ -700,7 +694,7 @@ def test_untagged() -> None:
     class Foo:
         a: Union[Bar, Baz]
         b: Union[int, str]
-        c: Union[Dict[int, str], List[int]]
+        c: Union[dict[int, str], list[int]]
         d: Union[int, Nested]
 
     f = Foo(Bar(10), "foo", {10: "bar"}, Nested(Bar(100)))
@@ -718,10 +712,10 @@ def test_untagged() -> None:
     with pytest.raises(Exception):
         assert to_dict(from_dict(Foo, d)) == f
 
-    # Untaggled can't differenciate Dict and List.
+    # Untaggled can't differenciate dict and list.
     @serde
     class Foo:
-        a: Union[List[int], Dict[int, str]]
+        a: Union[list[int], dict[int, str]]
 
     f = Foo({10: "bar"})
     with pytest.raises(Exception):
@@ -747,8 +741,8 @@ def test_newtype_and_untagged_union() -> None:
 
     @serde(tagging=Untagged)
     class MyDataclass:
-        unrelateds: List[SupposedlyUnrelated]
-        buggy_field: List[Union[str, Innerclass]]
+        unrelateds: list[SupposedlyUnrelated]
+        buggy_field: list[Union[str, Innerclass]]
 
     data = {"unrelateds": [], "buggy_field": [{"inner_value": "value"}, "something"]}
     actual = from_dict(MyDataclass, data)
@@ -789,7 +783,7 @@ def test_union_frozenset_with_prim() -> None:
     @serde
     @dataclass
     class Foo:
-        a: Union[FrozenSet[int], int]
+        a: Union[frozenset[int], int]
 
     assert to_dict(Foo(frozenset({1}))) == {"a": {1}}
 
@@ -801,12 +795,12 @@ def test_union_with_any() -> None:
 
     @dataclass
     class BarWithDict:
-        bar: Dict[str, Any]
+        bar: dict[str, Any]
 
     @serde(tagging=Untagged)
     @dataclass
     class Class:
-        foobars: List[Union[FooWithString, BarWithDict]]
+        foobars: list[Union[FooWithString, BarWithDict]]
 
     c = Class([FooWithString("string"), BarWithDict({"key": "value"})])
     assert c == from_json(Class, to_json(c))