4

I have the following code snippet:

from typing import TypedDict

class Super(TypedDict):
    foo: int

class SubA(Super):
    bar: int

class SubB(Super):
    zap: int

def print_props(inp: Super, key: str):
    print(inp[key])

When I call the method print_props with either an instance of SubA or SubB it would be valid as they are sub types of Super.

But mypy will complain about inp[key] as the key must be literal "foo". Is it possible to give mypy hints so that it is capable of deciding which keys are valid? For example: "When print_props is called with an instance of SubB only "foo" and "zap" are valid."

I took a look at generics; I think it is possible to declare a type variable that is restricted to sub types of Super, but is it possible to express the dependency between the concrete type of the type variable (SubA or SubB) and the literal values key should then be restricted to?

1
  • 1
    SubA and SubB aren't subtypes of Super, though. They subclass from TypedDict. Commented Oct 14, 2022 at 13:05

1 Answer 1

5

Overloads with Literal could well do it, though I do wonder if a different design would be better. I'm a bit concerned about the increasingly frequent usage of overload and Literal in SO answers. They both suggest a design smell to me

@overload
def printMyProps(input: SubA, key: Literal["foo", "bar"]) -> None: ...

@overload
def printMyProps(input: SubB, key: Literal["foo", "zap"]) -> None: ...

def printMyProps(input: SubA | SubB, key: Literal["foo", "bar", "zap"]) -> None:
  print(input[key])  # type: ignore

I've used type: ignore because it's a short function and I can't use isinstance on TypedDict. TBH overload implementations often require type hacks. The API works as intended though

Sign up to request clarification or add additional context in comments.

3 Comments

This is the right way to do it. Personally, I think overload is perfectly fine. I think introducing TypedDict was the mistake. It basically nudges people towards misusing dictionaries instead of using objects. It encourages artificially restricting a perfectly nice generic mapping type (dict) in situations where you should just create your own class with the attributes you need. Not a fan.
@DaniilFajnberg For me TypedDict is a good 'utility' type to make legacy code that deals a lot with dictionaries typesafe. I think the solution provided by @joel is the right one for me. Anyway I miss an operator like keyof as TypeScript provides it . A keyof like operator would make it possible to define the valid keys dependent from the type of input.
@DaniilFajnberg TypedDict is needed to describe things like JSON-based interfaces that are defined to return dicts with a specific structure. Otherwise, ... @dataclass is your friend! Except ... I came here looking for keyof, because I want a type based on another @dataclass-based class, except typed with values all bool.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.