Skip to content

PEP 695 type alias not narrowed through if x in (...) __contains__ check #2903

@SebbyLaw

Description

@SebbyLaw

Summary

ty fails to narrow a PEP 695 type alias through an if x in (...) containment check, resulting in @Todo instead of the correctly narrowed type. The same code works with a bare Literal assignment or TypeAlias.

Minimal repro

Playground link: https://play.ty.dev/7b5eee9d-35c1-4f5c-8b04-a570fdd34cf4

from typing import Literal, assert_never

type Foo = Literal["a", "b", "c", "d"]

def f(x: Foo) -> str:
    if x in ("a", "b"):
        return "AB"
    match x:
        case "c":
            return "C"
        case "d":
            return "D"
        case _ as never:
            assert_never(never)  # error: Inferred type of argument is `@Todo`
error[type-assertion-failure]: Argument does not have asserted type `Never`
  --> main.py:15:13
   |
13 |             return "D"
14 |         case _ as never:
15 |             assert_never(never)
   |             ^^^^^^^^^^^^^-----^
   |                          |
   |                          Inferred type of argument is `@Todo`

What works

All of the following pass without errors:

# 1. Bare Literal (no alias) + if-in: OK
Bar = Literal["a", "b", "c", "d"]

def g(x: Bar) -> str:
    if x in ("a", "b"):
        return "AB"
    match x:
        case "c":
            return "C"
        case "d":
            return "D"
        case _ as never:
            assert_never(never)  # OK

# 2. PEP 695 + match only (no if-in): OK
def h(x: Foo) -> str:
    match x:
        case "a":
            return "A"
        case "b":
            return "B"
        case "c":
            return "C"
        case "d":
            return "D"
        case _ as never:
            assert_never(never)  # OK

# 3. PEP 695 + if == (not if-in): OK
def i(x: Foo) -> str:
    if x == "a":
        return "A"
    match x:
        case "b":
            return "B"
        case "c":
            return "C"
        case "d":
            return "D"
        case _ as never:
            assert_never(never)  # OK

Version

ty 0.0.18 (7516727 2026-02-20)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions