jpinner-lyft: ```
>>> from pynamodb.attributes import UnicodeAttribute, UnicodeSetAttribute, ListAttribute
>>> from pynamodb.models import Model
>>> class UnicodeSetListModel(Model):
...     foo = UnicodeAttribute(hash_key=True)
...     usl = ListAttribute(of=UnicodeSetAttribute)
... 
>>> uslm = UnicodeSetListModel()
>>> uslm.foo = 'foo'
>>> uslm.usl = [{'Hello'}, {'World'}, {'!'}]
>>> uslm._serialize()
{'attributes': {'usl': {'L': [{'SS': ['Hello']}, {'SS': ['World']}, {'SS': ['!']}]}}, 'HASH': 'foo'}
>>>
>>> uslm._deserialize({'usl': {'L': [{'SS': ['Hello']}, {'SS': ['World', '!']}]}})
>>> uslm.usl
[{'Hello'}, {'World', '!'}]
>>>
>>> uslm._deserialize({'usl': {'L': [{'SS': ['Hello']}, {'S': 'World!'}]}})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jpinner/workspace/PynamoDB/pynamodb/models.py", line 1128, in _deserialize
    value = attr.deserialize(value)
  File "/Users/jpinner/workspace/PynamoDB/pynamodb/attributes.py", line 975, in deserialize
    raise ValueError("Cannot deserialize elements of type: {}".format(attr_type))
ValueError: Cannot deserialize elements of type: S
```
diff --git a/docs/attributes.rst b/docs/attributes.rst
index ac216c2..8ae75d9 100644
--- a/docs/attributes.rst
+++ b/docs/attributes.rst
@@ -134,7 +134,7 @@ Creating an untyped list is done like so:
     GroceryList(store_name='Haight Street Market',
                 groceries=['bread', 1, 'butter', 6, 'milk', 1])
 
-PynamoDB can provide type safety if it is required. Currently PynamoDB does not allow type checks on anything other than ``MapAttribute`` and subclasses of ``MapAttribute``. We're working on adding more generic type checking in a future version.
+PynamoDB can provide type safety if it is required. Currently PynamoDB does not allow type checks on anything other than subclasses of ``Attribute``. We're working on adding more generic type checking in a future version.
 When defining your model use the ``of=`` kwarg and pass in a class. PynamoDB will check that all items in the list are of the type you require.
 
 .. code-block:: python
diff --git a/docs/release_notes.rst b/docs/release_notes.rst
index a2c53a1..fc3ad79 100644
--- a/docs/release_notes.rst
+++ b/docs/release_notes.rst
@@ -9,6 +9,7 @@ v5.0 (unreleased)
   cannot contain range keys, this had limited utility to begin with, and would sometimes cause confusing
   "'filter_condition' cannot contain key attributes" errors.
 * Replace the internal attribute type constants with their "short" DynamoDB version (#827)
+* Typed list attributes can now support any Attribute subclass (#833)
 
 v4.3.3
 ----------
diff --git a/pynamodb/attributes.py b/pynamodb/attributes.py
index cd2379b..800a590 100644
--- a/pynamodb/attributes.py
+++ b/pynamodb/attributes.py
@@ -874,7 +874,7 @@ def _get_value_for_deserialize(value):
 
 
 def _get_class_for_deserialize(value):
-    value_type = list(value.keys())[0]
+    value_type = next(iter(value.keys()))
     if value_type not in DESERIALIZE_CLASS_MAP:
         raise ValueError('Unknown value: ' + str(value))
     return DESERIALIZE_CLASS_MAP[value_type]
@@ -922,7 +922,7 @@ class ListAttribute(Generic[_T], Attribute[List[_T]]):
         null: Optional[bool] = None,
         default: Optional[Union[Any, Callable[..., Any]]] = None,
         attr_name: Optional[str] = None,
-        of: Optional[Type[_T]]=None,
+        of: Optional[Type[_T]] = None,
     ) -> None:
         super().__init__(
             hash_key=hash_key,
@@ -932,8 +932,8 @@ class ListAttribute(Generic[_T], Attribute[List[_T]]):
             attr_name=attr_name,
         )
         if of:
-            if not issubclass(of, MapAttribute):
-                raise ValueError("'of' must be subclass of MapAttribute")
+            if not issubclass(of, Attribute):
+                raise ValueError("'of' must be a subclass of Attribute")
             self.element_type = of
 
     def remove_indexes(self, *indexes):
@@ -947,10 +947,16 @@ class ListAttribute(Generic[_T], Attribute[List[_T]]):
         """
         rval = []
         for v in values:
-            attr_class = (self.element_type()
-                          if self.element_type
-                          else _get_class_for_serialize(v))
-            rval.append({attr_class.attr_type: attr_class.serialize(v)})
+            attr_class = self._get_serialize_class(v)
+            if self.element_type and v is not None and not isinstance(attr_class, self.element_type):
+                raise ValueError("List elements must be of type: {}".format(self.element_type.__name__))
+            attr_type = attr_class.attr_type
+            attr_value = attr_class.serialize(v)
+            if attr_value is None:
+                # When attribute values serialize to "None" (e.g. empty sets) we store {"NULL": True} in DynamoDB.
+                attr_type = NULL
+                attr_value = True
+            rval.append({attr_type: attr_value})
         return rval
 
     def deserialize(self, values):
@@ -959,23 +965,44 @@ class ListAttribute(Generic[_T], Attribute[List[_T]]):
         """
         deserialized_lst = []
         for v in values:
-            class_for_deserialize = self.element_type() if self.element_type else _get_class_for_deserialize(v)
-            attr_value = _get_value_for_deserialize(v)
-            deserialized_lst.append(class_for_deserialize.deserialize(attr_value))
+            attr_type, attr_value = next(iter(v.items()))
+            attr_class = self._get_deserialize_class(attr_type)
+            if attr_class.attr_type != attr_type:
+                raise ValueError("Cannot deserialize {} elements from type: {}".format(
+                    attr_class.__class__.__name__, attr_type))
+            deserialized_lst.append(attr_class.deserialize(attr_value))
         return deserialized_lst
 
     def __getitem__(self, idx: int) -> Path:
         # for typing only
         return super().__getitem__(idx)
 
+    def _get_serialize_class(self, value):
+        if value is None:
+            return NullAttribute()
+        if isinstance(value, Attribute):
+            return value
+        if self.element_type:
+            return self.element_type()
+        return SERIALIZE_CLASS_MAP[type(value)]
+
+    def _get_deserialize_class(self, attr_type):
+        if self.element_type and attr_type != NULL:
+            return self.element_type()
+        return DESERIALIZE_CLASS_MAP[attr_type]
+
 
 DESERIALIZE_CLASS_MAP: Dict[str, Attribute] = {
+    BINARY: BinaryAttribute(),
+    BINARY_SET: BinarySetAttribute(),
+    BOOLEAN: BooleanAttribute(),
     LIST: ListAttribute(),
+    MAP: MapAttribute(),
+    NULL: NullAttribute(),
     NUMBER: NumberAttribute(),
+    NUMBER_SET: NumberSetAttribute(),
     STRING: UnicodeAttribute(),
-    BOOLEAN: BooleanAttribute(),
-    MAP: MapAttribute(),
-    NULL: NullAttribute()
+    STRING_SET: UnicodeSetAttribute()
 }
 
 SERIALIZE_CLASS_MAP = {

Why MapAttribute is required for ListAttribute?
I think `ListAttribute` could get common`Attribute` like `UnicodeAttribute`
But it couldn't.

On pynamodb/attributes.py#L560-563
```python
        if of:
            if not issubclass(of, MapAttribute):
                raise ValueError("'of' must be subclass of MapAttribute")
            self.element_type = of
```

And so on, in this document section, http://pynamodb.readthedocs.io/en/latest/attributes.html#list-attributes

```python
from pynamodb.attributes import ListAttribute, NumberAttribute, UnicodeAttribute

class Office(Model):
    class Meta:
        table_name = 'OfficeModel'
    office_id = NumberAttribute(hash_key=True)
    employees = ListAttribute(of=UnicodeAttribute)
```

I wanna know what a right intent is. Thanks
diff --git a/tests/test_attributes.py b/tests/test_attributes.py
index 9922fb6..1b1061e 100644
--- a/tests/test_attributes.py
+++ b/tests/test_attributes.py
@@ -903,6 +903,37 @@ class TestValueDeserialize:
         assert actual is None
 
 
+class TestListAttribute:
+
+    def test_untyped_list(self):
+        untyped_list = [{'Hello': 'World'}, ['!'], {'foo', 'bar'}, None, "", 0, False]
+        serialized = ListAttribute().serialize(untyped_list)
+        # set attributes are serialized as lists
+        untyped_list[2] = list(untyped_list[2])
+        assert ListAttribute().deserialize(serialized) == untyped_list
+
+    def test_list_of_strings(self):
+        string_list_attribute = ListAttribute(of=UnicodeAttribute)
+        string_list = ['foo', 'bar', 'baz']
+        serialized = string_list_attribute.serialize(string_list)
+        assert string_list_attribute.deserialize(serialized) == string_list
+
+    def test_list_type_error(self):
+        string_list_attribute = ListAttribute(of=UnicodeAttribute)
+
+        with pytest.raises(ValueError):
+            string_list_attribute.serialize([MapAttribute(foo='bar')])
+
+        with pytest.raises(ValueError):
+            string_list_attribute.deserialize([{'M': {'foo': {'S': 'bar'}}}])
+
+    def test_serialize_null(self):
+        string_set_list_attribute = ListAttribute(of=UnicodeSetAttribute)
+        list_with_empty_set = [{'foo'}, {}, None]
+        serialized = string_set_list_attribute.serialize(list_with_empty_set)
+        assert string_set_list_attribute.deserialize(serialized) == [{'foo'}, None, None]
+
+
 class TestMapAndListAttribute:
 
     def test_map_of_list(self):

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y gcc make sudo
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
botocore==1.37.23
coverage==7.8.0
exceptiongroup==1.2.2
execnet==2.1.1
iniconfig==2.1.0
jmespath==1.0.1
packaging==24.2
pluggy==1.5.0
-e git+https://github.com/pynamodb/PynamoDB.git@377e8f6f0cb6fd6416de1107e70cd25790663d44#egg=pynamodb
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
python-dateutil==2.9.0.post0
six==1.17.0
tomli==2.2.1
typing_extensions==4.13.0
urllib3==1.26.20

name: PynamoDB
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - botocore==1.37.23
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - iniconfig==2.1.0
      - jmespath==1.0.1
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - python-dateutil==2.9.0.post0
      - six==1.17.0
      - tomli==2.2.1
      - typing-extensions==4.13.0
      - urllib3==1.26.20
prefix: /opt/conda/envs/PynamoDB

tests/test_attributes.py::TestListAttribute::test_list_of_strings tests/test_attributes.py::TestListAttribute::test_list_type_error tests/test_attributes.py::TestListAttribute::test_serialize_null
tests/test_attributes.py::TestAttributeDescriptor::test_binary_attr tests/test_attributes.py::TestAttributeDescriptor::test_binary_set_attr tests/test_attributes.py::TestAttributeDescriptor::test_number_attr tests/test_attributes.py::TestAttributeDescriptor::test_number_set_attr tests/test_attributes.py::TestAttributeDescriptor::test_unicode_attr tests/test_attributes.py::TestAttributeDescriptor::test_unicode_set_attr tests/test_attributes.py::TestAttributeDescriptor::test_datetime_attr tests/test_attributes.py::TestAttributeDescriptor::test_bool_attr tests/test_attributes.py::TestAttributeDescriptor::test_json_attr
tests/test_attributes.py::TestUTCDateTimeAttribute::test_utc_datetime_attribute tests/test_attributes.py::TestUTCDateTimeAttribute::test_utc_date_time_deserialize tests/test_attributes.py::TestUTCDateTimeAttribute::test_dateutil_parser_fallback tests/test_attributes.py::TestUTCDateTimeAttribute::test_utc_date_time_deserialize_parse_args tests/test_attributes.py::TestUTCDateTimeAttribute::test_utc_date_time_serialize tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_roundtrips tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_no_microseconds tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2.47-01-06T08:21:00.0+0000] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06T08:21:00.+0000] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06T08:21:00.0] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06 tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[abcd-01-06T08:21:00.0+0000] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-ab-06T08:21:00.0+0000] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-abT08:21:00.0+0000] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06Tab:21:00.0+0000] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06T08:ab:00.0+0000_0] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06T08:ab:00.0+0000_1] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06T08:21:00.a+0000] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06T08:21:00.0.1+0000] tests/test_attributes.py::TestUTCDateTimeAttribute::test__fast_parse_utc_datestring_invalid_input[2047-01-06T08:21:00.0+00000] tests/test_attributes.py::TestBinaryAttribute::test_binary_attribute tests/test_attributes.py::TestBinaryAttribute::test_binary_round_trip tests/test_attributes.py::TestBinaryAttribute::test_binary_serialize tests/test_attributes.py::TestBinaryAttribute::test_binary_deserialize tests/test_attributes.py::TestBinaryAttribute::test_binary_set_serialize tests/test_attributes.py::TestBinaryAttribute::test_binary_set_round_trip tests/test_attributes.py::TestBinaryAttribute::test_binary_set_deserialize tests/test_attributes.py::TestBinaryAttribute::test_binary_set_attribute tests/test_attributes.py::TestNumberAttribute::test_number_attribute tests/test_attributes.py::TestNumberAttribute::test_number_serialize tests/test_attributes.py::TestNumberAttribute::test_number_deserialize tests/test_attributes.py::TestNumberAttribute::test_number_set_deserialize tests/test_attributes.py::TestNumberAttribute::test_number_set_serialize tests/test_attributes.py::TestNumberAttribute::test_number_set_attribute tests/test_attributes.py::TestUnicodeAttribute::test_unicode_attribute tests/test_attributes.py::TestUnicodeAttribute::test_unicode_serialize tests/test_attributes.py::TestUnicodeAttribute::test_unicode_deserialize tests/test_attributes.py::TestUnicodeAttribute::test_unicode_set_serialize tests/test_attributes.py::TestUnicodeAttribute::test_round_trip_unicode_set tests/test_attributes.py::TestUnicodeAttribute::test_unicode_set_deserialize tests/test_attributes.py::TestUnicodeAttribute::test_unicode_set_attribute tests/test_attributes.py::TestBooleanAttribute::test_boolean_attribute tests/test_attributes.py::TestBooleanAttribute::test_boolean_serialize tests/test_attributes.py::TestBooleanAttribute::test_boolean_deserialize tests/test_attributes.py::TestTTLAttribute::test_default_and_default_for_new tests/test_attributes.py::TestTTLAttribute::test_timedelta_ttl tests/test_attributes.py::TestTTLAttribute::test_datetime_naive_ttl tests/test_attributes.py::TestTTLAttribute::test_datetime_with_tz_ttl tests/test_attributes.py::TestTTLAttribute::test_ttl_attribute_wrong_type tests/test_attributes.py::TestTTLAttribute::test_serialize_timedelta tests/test_attributes.py::TestTTLAttribute::test_serialize_none tests/test_attributes.py::TestTTLAttribute::test_serialize_deserialize tests/test_attributes.py::TestJSONAttribute::test_quoted_json tests/test_attributes.py::TestJSONAttribute::test_json_attribute tests/test_attributes.py::TestJSONAttribute::test_json_serialize tests/test_attributes.py::TestJSONAttribute::test_json_deserialize tests/test_attributes.py::TestJSONAttribute::test_control_chars tests/test_attributes.py::TestMapAttribute::test_attribute_children tests/test_attributes.py::TestMapAttribute::test_null_attribute_raw_map tests/test_attributes.py::TestMapAttribute::test_null_attribute_subclassed_map tests/test_attributes.py::TestMapAttribute::test_null_attribute_map_after_serialization tests/test_attributes.py::TestMapAttribute::test_map_of_map tests/test_attributes.py::TestMapAttribute::test_map_overridden_attrs_accessors tests/test_attributes.py::TestMapAttribute::test_map_overridden_attrs_serialize tests/test_attributes.py::TestMapAttribute::test_additional_attrs_deserialize tests/test_attributes.py::TestMapAttribute::test_defaults tests/test_attributes.py::TestMapAttribute::test_raw_set_attr tests/test_attributes.py::TestMapAttribute::test_raw_set_item tests/test_attributes.py::TestMapAttribute::test_raw_map_from_dict tests/test_attributes.py::TestMapAttribute::test_raw_map_access tests/test_attributes.py::TestMapAttribute::test_raw_map_iter tests/test_attributes.py::TestMapAttribute::test_raw_map_json_serialize tests/test_attributes.py::TestMapAttribute::test_typed_and_raw_map_json_serialize tests/test_attributes.py::TestMapAttribute::test_json_serialize tests/test_attributes.py::TestMapAttribute::test_serialize_datetime tests/test_attributes.py::TestMapAttribute::test_complex_map_accessors tests/test_attributes.py::TestMapAttribute::test_attribute_paths_subclassing tests/test_attributes.py::TestMapAttribute::test_attribute_paths_wrapping tests/test_attributes.py::TestValueDeserialize::test__get_value_for_deserialize tests/test_attributes.py::TestValueDeserialize::test__get_value_for_deserialize_null tests/test_attributes.py::TestListAttribute::test_untyped_list tests/test_attributes.py::TestMapAndListAttribute::test_map_of_list tests/test_attributes.py::TestMapAndListAttribute::test_map_of_list_of_map tests/test_attributes.py::TestMapAndListAttribute::test_list_of_map_with_of tests/test_attributes.py::TestMapAndListAttribute::test_list_of_map_with_of_and_custom_attribute tests/test_attributes.py::TestVersionAttribute::test_serialize tests/test_attributes.py::TestVersionAttribute::test_deserialize
docs/release_notes.rst docs/attributes.rst pynamodb/attributes.py
docs/release_notes.rst docs/attributes.rst pynamodb/attributes.py