Here is the problem:
I have a dictionary that keeps track of two different types of values (let's say city and village) using two different types of keys (city_key and village_key). I'd like to annotate this dictionary with generics, so that when the dictionary receives key of type city_key mypy/Pyright should annotate the return value as city. Likewise, if you try to assign a city value to a village_key, mypy/pyright should throw an error.
The alternative is to maintain two different dictionaries, one for cities and one for villages, but I am curious if I can get away with one dictionary.
What I've tried so far
I researched this issue, but couldn't find a definitive example / answer. There is a question just like mine here, but it went unanswered.
Some pseudo code to show what I am aiming for in practice
# two types of aliased keys
# ... edited to use NewType as per juanpa.arrivillaga comment
CityKey = NewType("CityKey", str)
VillageKey = NewType("VillageKey", str)
# two types of values, city and village
class City:...
class Village:...
# key generator that returns city or village key based on type of input
def generate_key(settlement: City | Village) -> CityKey | VillageKey: ...
# declare some keys & values
london = City("London")
london_key = generate_key(london)
mousehole = Village("Mousehole")
mousehole_key = generate_key(village)
# instantiate the dictionary
data: [????] = {}
# assign city to city key, and village to village key
data[london_key] = london
data[mousehole_key] = mousehole
# trying to assign village to city key should raise a type check error
data[london_key] = mousehole
# type of value accessed by village key should be village
reveal_type(data[mousehole_key]) # Type[Village]
Thank you!
PS
I am not sure if I am using the term covariant properly in the title. Let me know if I am not.