diff --git a/pandas_dataclasses/core/typing.py b/pandas_dataclasses/core/typing.py
index a97a6a0..161ce48 100644
--- a/pandas_dataclasses/core/typing.py
+++ b/pandas_dataclasses/core/typing.py
@@ -2,6 +2,7 @@ __all__ = ["Attr", "Column", "Data", "Index", "Other"]
 
 
 # standard library
+import types
 from dataclasses import Field
 from enum import Enum, auto
 from itertools import chain
@@ -149,6 +150,9 @@ def get_dtype(tp: Any) -> Optional[str]:
     if dtype is Any or dtype is type(None):
         return None
 
+    if is_union_type(dtype):
+        dtype = get_args(dtype)[0]
+
     if get_origin(dtype) is Literal:
         dtype = get_args(dtype)[0]
 
@@ -179,3 +183,12 @@ def get_role(tp: Any, default: Role = Role.OTHER) -> Role:
         return get_annotations(tp)[0]  # type: ignore
     except TypeError:
         return default
+
+
+def is_union_type(tp: Any) -> bool:
+    """Check if a type hint is a union type."""
+    if get_origin(tp) is Union:
+        return True
+
+    UnionType = getattr(types, "UnionType", None)
+    return UnionType is not None and isinstance(tp, UnionType)

diff --git a/tests/test_typing.py b/tests/test_typing.py
index de6d646..4ef0e43 100644
--- a/tests/test_typing.py
+++ b/tests/test_typing.py
@@ -16,12 +16,14 @@ testdata_dtype = [
     (Data[Any], None),
     (Data[None], None),
     (Data[int], np.dtype("i8")),
+    (Data[Union[int, None]], np.dtype("i8")),
     (Data[L["i8"]], np.dtype("i8")),
     (Data[L["boolean"]], pd.BooleanDtype()),
     (Data[L["category"]], pd.CategoricalDtype()),
     (Index[Any], None),
     (Index[None], None),
     (Index[int], np.dtype("i8")),
+    (Index[Union[int, None]], np.dtype("i8")),
     (Index[L["i8"]], np.dtype("i8")),
     (Index[L["boolean"]], pd.BooleanDtype()),
     (Index[L["category"]], pd.CategoricalDtype()),

Support for union type in type hints
Update typing module to allow users to use union type in `Data` and `Index`:
```python
@dataclass
class Weather(AsFrame):
    """Weather information."""

    year: Index[int]
    month: Index[int]
    temp: Data[float | None]
    wind: Data[float | None]
```
See also #115 for related discussions.
tests/test_typing.py::test_get_dtype[tp3-dtype3] tests/test_typing.py::test_get_dtype[tp10-dtype10]
tests/test_typing.py::test_get_dtype[tp0-None] tests/test_typing.py::test_get_dtype[tp1-None] tests/test_typing.py::test_get_dtype[tp2-dtype2] tests/test_typing.py::test_get_dtype[tp4-dtype4] tests/test_typing.py::test_get_dtype[tp5-dtype5] tests/test_typing.py::test_get_dtype[tp6-dtype6] tests/test_typing.py::test_get_dtype[tp7-None] tests/test_typing.py::test_get_dtype[tp8-None] tests/test_typing.py::test_get_dtype[tp9-dtype9] tests/test_typing.py::test_get_dtype[tp11-dtype11] tests/test_typing.py::test_get_dtype[tp12-dtype12] tests/test_typing.py::test_get_dtype[tp13-dtype13] tests/test_typing.py::test_get_dtype[tp14-dtype14] tests/test_typing.py::test_get_dtype[tp15-dtype15] tests/test_typing.py::test_get_dtype[tp16-dtype16] tests/test_typing.py::test_get_dtype[tp17-dtype17] tests/test_typing.py::test_get_name[tp0-None] tests/test_typing.py::test_get_name[tp1-None] tests/test_typing.py::test_get_name[tp2-None] tests/test_typing.py::test_get_name[tp3-None] tests/test_typing.py::test_get_name[tp4-None] tests/test_typing.py::test_get_name[tp5-attr] tests/test_typing.py::test_get_name[tp6-column] tests/test_typing.py::test_get_name[tp7-data] tests/test_typing.py::test_get_name[tp8-index] tests/test_typing.py::test_get_name[tp9-None] tests/test_typing.py::test_get_name[tp10-None] tests/test_typing.py::test_get_name[tp11-None] tests/test_typing.py::test_get_name[tp12-None] tests/test_typing.py::test_get_name[tp13-None] tests/test_typing.py::test_get_name[tp14-None] tests/test_typing.py::test_get_name[tp15-attr] tests/test_typing.py::test_get_name[tp16-column] tests/test_typing.py::test_get_name[tp17-data] tests/test_typing.py::test_get_name[tp18-index] tests/test_typing.py::test_get_name[tp19-None] tests/test_typing.py::test_get_role[tp0-Role.ATTR] tests/test_typing.py::test_get_role[tp1-Role.COLUMN] tests/test_typing.py::test_get_role[tp2-Role.DATA] tests/test_typing.py::test_get_role[tp3-Role.INDEX] tests/test_typing.py::test_get_role[tp4-Role.OTHER] tests/test_typing.py::test_get_role[tp5-Role.ATTR] tests/test_typing.py::test_get_role[tp6-Role.COLUMN] tests/test_typing.py::test_get_role[tp7-Role.DATA] tests/test_typing.py::test_get_role[tp8-Role.INDEX] tests/test_typing.py::test_get_role[tp9-Role.OTHER] tests/test_typing.py::test_get_role[tp10-Role.ATTR] tests/test_typing.py::test_get_role[tp11-Role.COLUMN] tests/test_typing.py::test_get_role[tp12-Role.DATA] tests/test_typing.py::test_get_role[tp13-Role.INDEX] tests/test_typing.py::test_get_role[tp14-Role.OTHER]