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.