Search code examples
pythonpython-typingmypy

How can I type hint the return type of .values() in a custom dict subclass?


When I type check the following class with mypy it throws an error

error: Returning Any from function declared to return "Optional[SomeDictValueType]"

in get_by_id as the type of .values() isn't defined/restricted properly.

class SomeDict(dict):
    def __init__(self) -> None:
        self._some_attribute: SomeRandomType = None

    def get_by_id(self, id: str) -> Optional[SomeDictValueType]:
        for p in self.values():
            if p._id == id:
                return p
        return None

I found similar questions online and tried the following things:
  1. adding type hints to __setitem__ as explained here

     def __setitem__(self, key: str, value: SomeDictValueType) -> None:
         super(SomeDict, self).__setitem__(key, value)
    
  2. using Mapping in the class header as explained here

     class SomeDict(dict, Mapping[str, SomeDictValueType]): ...
    

How can I get rid of this error without using # type: ignore? Instances of this class will never hold values of a type other than SomeDictValueType.


EDIT:
Here is a minimalistic example to reproduce the error. Save the following code snippet to a python file and execute mypy --strict <filename>.py on it.

from typing import Optional

class SomeDict(dict):  # type: ignore
    def __init__(self) -> None:
        self._some_attribute: Optional[str] = None

    def get_by_id(self, id: str) -> Optional[int]:
        for p in self.values():
            if p._id == id:
                return p
        return None

The following error should be thrown:

test.py:10: error: Returning Any from function declared to return "Optional[int]"


Solution

  • I think, I just figured it out. I thought you cannot subclass from a typing definition (I tried it once at the beginning but got another error and didn't look close enough).
    In my opinion, the following snippet would be a solution for the minimalistic error reproducing example.

    from typing import Optional, Dict
    
    class SomeDict(Dict[str, int]):
        def __init__(self) -> None:
            self._some_attribute: Optional[str] = None
    
        def get_by_id(self, id: str) -> Optional[int]:
            for p in self.values():
                if p == 5:
                    return p
            return None