How to Fix Autocomplete and Type Hints for Subclassed Generic Dictionaries in Python
If you are creating custom dictionary classes in Python 3.12+ using the PEP 695 type parameter syntax, you might encounter a frustrating issue: your IDE (such as VS Code with Pylance or Pyright) provides full autocomplete for direct variable attributes, but completely loses type hints when accessing elements via dictionary indexing like my_dict['key'].
The Cause of the Problem
In your implementation, you defined the generic dictionary class as follows:
class Items[K, I](dict):Although Python allows this syntax, inheriting directly from dict without supplying the generic parameters means you are inheriting from an unparameterized (or raw) dictionary, effectively equivalent to dict[Any, Any]. Because dict itself is generic, type checkers will infer that calling dict.__getitem__ returns Any rather than your specified type parameter I.
The Solution: Parameterize the Base Class
To restore full IDE autocomplete and enable correct static type checking, pass the generic type parameters K and I directly into the base dict[K, I] class definition.
Updated Python 3.12+ Code Solution
from typing import Self
# Parameterize dict with K and I
class Items[K, I](dict[K, I]):
def add_item(self, key: K, item: I):
# Additional custom logic can go here
self[key] = item
class Person(Items[str, 'Person']):
def __init__(self, name: str, parent: Self | None):
self.name = name
self.parent = parent
if parent is not None:
parent.add_item(self.name, self)
emily = Person('Emily', None)
hannah = Person('Hannah', emily)
print(hannah.name) # Autocomplete works
print(emily['Hannah'].name) # Autocomplete now works correctly!
print(hannah.parent.name) # Autocomplete works
Backward Compatible Solution (Python 3.11 and Earlier)
If your project supports Python versions prior to 3.12, use typing.TypeVar and inherit from dict[K, I] alongside Generic[K, I]:
from typing import TypeVar, Generic, Optional
from typing_extensions import Self
K = TypeVar('K')
I = TypeVar('I')
class Items(dict[K, I], Generic[K, I]):
def add_item(self, key: K, item: I) -> None:
self[key] = item
class Person(Items[str, 'Person']):
def __init__(self, name: str, parent: Optional[Self] = None):
self.name = name
self.parent = parent
if parent is not None:
parent.add_item(self.name, self)
Key Takeaways
- Always supply type parameters to generic base classes (e.g.,
dict[K, V]orlist[T]) when inheriting. - Inheriting from raw
dictcauses key lookups (__getitem__) to evaluate toAny, which disables IntelliSense features in VS Code, PyCharm, and Pyright.