#4551·ty

Enum class rejected by a generic union

Author: kalekseevCreated Sep 18, 2026Updated Sep 18, 2026

Summary

ty 0.0.82 rejects this call. ty 0.0.81 and Pyright 1.1.410 accept it.

python
from collections.abc import Iterable
from enum import Enum

class Choice(Enum):
    A = "a"

def accepts_choices[T](choices: Iterable[tuple[T, str]] | type[Enum]) -> None:
    pass

accepts_choices(Choice)
error[[invalid-argument-type](https://ty.dev/rules#invalid-argument-type)]: Argument to function `accepts_choices` is incorrect
  --> a.py:18:17
   |
18 | accepts_choices(Choice)
   |                 ^^^^^^ Expected `Iterable[tuple[Unknown, str]]`, found `<class 'Choice'>`
info: type `<class 'Choice'>` is not assignable to protocol `Iterable[tuple[Unknown, str]]`
info: └── protocol member `__iter__` is incompatible
info:     └── incompatible return types: `Iterator[Choice]` is not assignable to `Iterator[tuple[Unknown, str]]`
info:         └── protocol `Iterator[Choice]` is not assignable to protocol `Iterator[tuple[Unknown, str]]`
info:             └── protocol member `__next__` is incompatible
info:                 └── incompatible return types: `Choice` is not assignable to `tuple[Unknown, str]`
info: Function defined here
  --> a.py:14:5
   |
14 | def accepts_choices[T](choices: Iterable[tuple[T, str]] | type[Enum]) -> None:
   |     ^^^^^^^^^^^^^^^    --------------------------------------------- Parameter declared here

Expected: accept Choice through the type[Enum] union branch. That branch is absent from the diagnostic.

Replacing T with str, or using a legacy TypeVar with default=str, avoids the error. This also affects Django field declarations with choices=SomeTextChoices or choices=SomeIntegerChoices.

Version

ty 0.0.82 (6999edb88 2026-09-17)