I wanted to add a single special value to another type for a union type, something like this:
from typing import Literal UNMEASURED = "unmeasured" Unmeasured = Literal[UNMEASURED] Resource = int | Unmeasured notes: dict[Resource, str] = {1: "foo", 2: "bar", 3: "baz", UNMEASURED: "quux"} resources = [1, 2, 3] + [UNMEASURED] for resource in resources: print(notes[resource])
This works, but mypy complains: ‘error: Parameter 1 of Literal[…] is invalid [valid-type]’. So to make this pass mypy typechecking you have to repeat “unmeasured” as a literal. Even if you do that:
from typing import Literal UNMEASURED = "unmeasured" Unmeasured = Literal["unmeasured"] Resource = int | Unmeasured notes: dict[Resource, str] = {1: "foo", 2: "bar", 3: "baz", UNMEASURED: "quux"} resources = [1, 2, 3] + [UNMEASURED] for resource in resources: print(notes[resource])
mypy will complain: ‘Dict entry 3 has incompatible type “str”: “str”; expected “int | Literal[‘unmeasured’]”: “str” [dict-item]’ and also ‘Invalid index type “str | int” for “dict[int | Literal[‘unmeasured’], str]”; expected type “int | Literal[‘unmeasured’]” [index]’
Eventually I came up with:
from enum import Enum from typing import Literal class Aspect(Enum): UNMEASURED = "unmeasured" Resource = int | Aspect notes: dict[Resource, str] = {1: "foo", 2: "bar", 3: "baz", Aspect.UNMEASURED: "quux"} resources = [1, 2, 3] + [Aspect.UNMEASURED] for resource in resources: print(notes[resource])
which both produces the same result, and passes the typechecker. I don’t love the weird Enum hanging out on its own but it does at least put the magic string in one place only and pass. I wonder what would be better.