Files
ruff/crates/ty_python_semantic/resources/mdtest/call/union.md

28 KiB

Unions in calls

Union of return types

def _(flag: bool):
    if flag:
        def f() -> int:
            return 1
    else:
        def f() -> str:
            return "foo"
    reveal_type(f())  # revealed: int | str

Calling with an unknown union

from nonexistent import f  # error: [unresolved-import] "Cannot resolve imported module `nonexistent`"

def coinflip() -> bool:
    return True

if coinflip():
    def f() -> int:
        return 1

reveal_type(f())  # revealed: Unknown | int

Non-callable elements in a union

Calling a union with a non-callable element should emit a diagnostic.

def _(flag: bool):
    if flag:
        f = 1
    else:
        def f() -> int:
            return 1
    x = f()  # error: [call-non-callable] "Object of type `Literal[1]` is not callable"
    reveal_type(x)  # revealed: Unknown | int

Multiple non-callable elements in a union

Calling a union with multiple non-callable elements should mention all of them in the diagnostic.

def _(flag: bool, flag2: bool):
    if flag:
        f = 1
    elif flag2:
        f = "foo"
    else:
        def f() -> int:
            return 1
    # error: [call-non-callable] "Object of type `Literal[1]` is not callable"
    # error: [call-non-callable] "Object of type `Literal["foo"]` is not callable"
    # revealed: Unknown | int
    reveal_type(f())

All non-callable union elements

Calling a union with no callable elements can emit a simpler diagnostic.

def _(flag: bool):
    if flag:
        f = 1
    else:
        f = "foo"

    x = f()  # error: [call-non-callable] "Object of type `Literal[1, "foo"]` is not callable"
    reveal_type(x)  # revealed: Unknown

Mismatching signatures

Calling a union where the arguments don't match the signature of all variants.

def f1(a: int) -> int:
    return a

def f2(a: str) -> str:
    return a

def _(flag: bool):
    if flag:
        f = f1
    else:
        f = f2

    # error: [invalid-argument-type] "Argument to function `f2` is incorrect: Expected `str`, found `Literal[3]`"
    x = f(3)
    reveal_type(x)  # revealed: int | str

Any non-callable variant

def f1(a: int): ...
def _(flag: bool):
    if flag:
        f = f1
    else:
        f = "This is a string literal"

    # error: [call-non-callable] "Object of type `Literal["This is a string literal"]` is not callable"
    x = f(3)
    reveal_type(x)  # revealed: Unknown

Union of binding errors

def f1(): ...
def f2(): ...
def _(flag: bool):
    if flag:
        f = f1
    else:
        f = f2

    # error: [too-many-positional-arguments] "Too many positional arguments to function `f1`: expected 0, got 1"
    # error: [too-many-positional-arguments] "Too many positional arguments to function `f2`: expected 0, got 1"
    x = f(3)
    reveal_type(x)  # revealed: Unknown

One not-callable, one wrong argument

class C: ...

def f1(): ...
def _(flag: bool):
    if flag:
        f = f1
    else:
        f = C()

    # error: [too-many-positional-arguments] "Too many positional arguments to function `f1`: expected 0, got 1"
    # error: [call-non-callable] "Object of type `C` is not callable"
    x = f(3)
    reveal_type(x)  # revealed: Unknown

Union including a special-cased function

def _(flag: bool):
    if flag:
        f = str
    else:
        f = repr
    reveal_type(str("string"))  # revealed: Literal["string"]
    reveal_type(repr("string"))  # revealed: Literal["'string'"]
    reveal_type(f("string"))  # revealed: Literal["string", "'string'"]

Unions with literals and negations

from typing import Literal
from ty_extensions import Not, AlwaysFalsy, static_assert, is_subtype_of, is_assignable_to

static_assert(is_subtype_of(Literal["a", ""], Literal["a", ""] | Not[AlwaysFalsy]))
static_assert(is_subtype_of(Not[AlwaysFalsy], Literal["", "a"] | Not[AlwaysFalsy]))
static_assert(is_subtype_of(Literal["a", ""], Not[AlwaysFalsy] | Literal["a", ""]))
static_assert(is_subtype_of(Not[AlwaysFalsy], Not[AlwaysFalsy] | Literal["a", ""]))

static_assert(is_subtype_of(Literal["a", ""], Literal["a", ""] | Not[Literal[""]]))
static_assert(is_subtype_of(Not[Literal[""]], Literal["a", ""] | Not[Literal[""]]))
static_assert(is_subtype_of(Literal["a", ""], Not[Literal[""]] | Literal["a", ""]))
static_assert(is_subtype_of(Not[Literal[""]], Not[Literal[""]] | Literal["a", ""]))

def _(
    a: Literal["a", ""] | Not[AlwaysFalsy],
    b: Literal["a", ""] | Not[Literal[""]],
    c: Literal[""] | Not[Literal[""]],
    d: Not[Literal[""]] | Literal[""],
    e: Literal["a"] | Not[Literal["a"]],
    f: Literal[b"b"] | Not[Literal[b"b"]],
    g: Not[Literal[b"b"]] | Literal[b"b"],
    h: Literal[42] | Not[Literal[42]],
    i: Not[Literal[42]] | Literal[42],
):
    reveal_type(a)  # revealed: Literal[""] | ~AlwaysFalsy
    reveal_type(b)  # revealed: object
    reveal_type(c)  # revealed: object
    reveal_type(d)  # revealed: object
    reveal_type(e)  # revealed: object
    reveal_type(f)  # revealed: object
    reveal_type(g)  # revealed: object
    reveal_type(h)  # revealed: object
    reveal_type(i)  # revealed: object

Cannot use an argument as both a value and a type form

from ty_extensions import is_singleton

def _(flag: bool):
    if flag:
        f = repr
    else:
        f = is_singleton
    # error: [conflicting-argument-forms] "Argument is used as both a value and a type form in call"
    reveal_type(f(int))  # revealed: str | Literal[False]

Size limit on unions of literals

Beyond a certain size, large unions of literal types collapse to their nearest super-type (int, bytes, str).

from typing import Literal

def _(literals_2: Literal[0, 1], b: bool, flag: bool):
    literals_4 = 2 * literals_2 + literals_2  # Literal[0, 1, 2, 3]
    literals_16 = 4 * literals_4 + literals_4  # Literal[0, 1, .., 15]
    literals_64 = 4 * literals_16 + literals_4  # Literal[0, 1, .., 63]
    literals_128 = 2 * literals_64 + literals_2  # Literal[0, 1, .., 127]
    literals_256 = 2 * literals_128 + literals_2  # Literal[0, 1, .., 255]

    # Going beyond the MAX_NON_RECURSIVE_UNION_LITERALS limit (currently 256):
    reveal_type(literals_256 if flag else 256)  # revealed: int

    # Going beyond the limit when another type is already part of the union
    bool_and_literals_128 = b if flag else literals_128  # bool | Literal[0, 1, ..., 127]
    literals_128_shifted = literals_128 + 128  # Literal[128, 129, ..., 255]
    literals_256_shifted = literals_256 + 256  # Literal[256, 257, ..., 511]

    # Now union the two:
    two = bool_and_literals_128 if flag else literals_128_shifted
    # revealed: bool | Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]
    reveal_type(two)
    reveal_type(two if flag else literals_256_shifted)  # revealed: int

Recursively defined literal union types are widened earlier than non-recursively defined types for faster convergence.

class RecursiveAttr:
    def __init__(self):
        self.i = 0

    def update(self):
        self.i = self.i + 1

reveal_type(RecursiveAttr().i)  # revealed: Unknown | int

# Here are some recursive but saturating examples. Because it's difficult to statically determine whether literal unions saturate or diverge,
# we widen them early, even though they may actually be convergent.
class RecursiveAttr2:
    def __init__(self):
        self.i = 0

    def update(self):
        self.i = (self.i + 1) % 9

reveal_type(RecursiveAttr2().i)  # revealed: Unknown | Literal[0, 1, 2, 3, 4, 5, 6, 7, 8]

class RecursiveAttr3:
    def __init__(self):
        self.i = 0

    def update(self):
        self.i = (self.i + 1) % 10

# Going beyond the MAX_RECURSIVE_UNION_LITERALS limit:
reveal_type(RecursiveAttr3().i)  # revealed: Unknown | int

We set a much higher limit for non-recursive unions of enum literals, because huge enums are common in generated code and it becomes frustrating if reachability analysis fails when matching over these enums:

from enum import Enum
from ty_extensions import Intersection, Not

class Huge(Enum):
    OPTION0 = "0"
    OPTION1 = "1"
    OPTION2 = "2"
    OPTION3 = "3"
    OPTION4 = "4"
    OPTION5 = "5"
    OPTION6 = "6"
    OPTION7 = "7"
    OPTION8 = "8"
    OPTION9 = "9"
    OPTION10 = "10"
    OPTION11 = "11"
    OPTION12 = "12"
    OPTION13 = "13"
    OPTION14 = "14"
    OPTION15 = "15"
    OPTION16 = "16"
    OPTION17 = "17"
    OPTION18 = "18"
    OPTION19 = "19"
    OPTION20 = "20"
    OPTION21 = "21"
    OPTION22 = "22"
    OPTION23 = "23"
    OPTION24 = "24"
    OPTION25 = "25"
    OPTION26 = "26"
    OPTION27 = "27"
    OPTION28 = "28"
    OPTION29 = "29"
    OPTION30 = "30"
    OPTION31 = "31"
    OPTION32 = "32"
    OPTION33 = "33"
    OPTION34 = "34"
    OPTION35 = "35"
    OPTION36 = "36"
    OPTION37 = "37"
    OPTION38 = "38"
    OPTION39 = "39"
    OPTION40 = "40"
    OPTION41 = "41"
    OPTION42 = "42"
    OPTION43 = "43"
    OPTION44 = "44"
    OPTION45 = "45"
    OPTION46 = "46"
    OPTION47 = "47"
    OPTION48 = "48"
    OPTION49 = "49"
    OPTION50 = "50"
    OPTION51 = "51"
    OPTION52 = "52"
    OPTION53 = "53"
    OPTION54 = "54"
    OPTION55 = "55"
    OPTION56 = "56"
    OPTION57 = "57"
    OPTION58 = "58"
    OPTION59 = "59"
    OPTION60 = "60"
    OPTION61 = "61"
    OPTION62 = "62"
    OPTION63 = "63"
    OPTION64 = "64"
    OPTION65 = "65"
    OPTION66 = "66"
    OPTION67 = "67"
    OPTION68 = "68"
    OPTION69 = "69"
    OPTION70 = "70"
    OPTION71 = "71"
    OPTION72 = "72"
    OPTION73 = "73"
    OPTION74 = "74"
    OPTION75 = "75"
    OPTION76 = "76"
    OPTION77 = "77"
    OPTION78 = "78"
    OPTION79 = "79"
    OPTION80 = "80"
    OPTION81 = "81"
    OPTION82 = "82"
    OPTION83 = "83"
    OPTION84 = "84"
    OPTION85 = "85"
    OPTION86 = "86"
    OPTION87 = "87"
    OPTION88 = "88"
    OPTION89 = "89"
    OPTION90 = "90"
    OPTION91 = "91"
    OPTION92 = "92"
    OPTION93 = "93"
    OPTION94 = "94"
    OPTION95 = "95"
    OPTION96 = "96"
    OPTION97 = "97"
    OPTION98 = "98"
    OPTION99 = "99"
    OPTION100 = "100"
    OPTION101 = "101"
    OPTION102 = "102"
    OPTION103 = "103"
    OPTION104 = "104"
    OPTION105 = "105"
    OPTION106 = "106"
    OPTION107 = "107"
    OPTION108 = "108"
    OPTION109 = "109"
    OPTION110 = "110"
    OPTION111 = "111"
    OPTION112 = "112"
    OPTION113 = "113"
    OPTION114 = "114"
    OPTION115 = "115"
    OPTION116 = "116"
    OPTION117 = "117"
    OPTION118 = "118"
    OPTION119 = "119"
    OPTION120 = "120"
    OPTION121 = "121"
    OPTION122 = "122"
    OPTION123 = "123"
    OPTION124 = "124"
    OPTION125 = "125"
    OPTION126 = "126"
    OPTION127 = "127"
    OPTION128 = "128"
    OPTION129 = "129"
    OPTION130 = "130"
    OPTION131 = "131"
    OPTION132 = "132"
    OPTION133 = "133"
    OPTION134 = "134"
    OPTION135 = "135"
    OPTION136 = "136"
    OPTION137 = "137"
    OPTION138 = "138"
    OPTION139 = "139"
    OPTION140 = "140"
    OPTION141 = "141"
    OPTION142 = "142"
    OPTION143 = "143"
    OPTION144 = "144"
    OPTION145 = "145"
    OPTION146 = "146"
    OPTION147 = "147"
    OPTION148 = "148"
    OPTION149 = "149"
    OPTION150 = "150"
    OPTION151 = "151"
    OPTION152 = "152"
    OPTION153 = "153"
    OPTION154 = "154"
    OPTION155 = "155"
    OPTION156 = "156"
    OPTION157 = "157"
    OPTION158 = "158"
    OPTION159 = "159"
    OPTION160 = "160"
    OPTION161 = "161"
    OPTION162 = "162"
    OPTION163 = "163"
    OPTION164 = "164"
    OPTION165 = "165"
    OPTION166 = "166"
    OPTION167 = "167"
    OPTION168 = "168"
    OPTION169 = "169"
    OPTION170 = "170"
    OPTION171 = "171"
    OPTION172 = "172"
    OPTION173 = "173"
    OPTION174 = "174"
    OPTION175 = "175"
    OPTION176 = "176"
    OPTION177 = "177"
    OPTION178 = "178"
    OPTION179 = "179"
    OPTION180 = "180"
    OPTION181 = "181"
    OPTION182 = "182"
    OPTION183 = "183"
    OPTION184 = "184"
    OPTION185 = "185"
    OPTION186 = "186"
    OPTION187 = "187"
    OPTION188 = "188"
    OPTION189 = "189"
    OPTION190 = "190"
    OPTION191 = "191"
    OPTION192 = "192"
    OPTION193 = "193"
    OPTION194 = "194"
    OPTION195 = "195"
    OPTION196 = "196"
    OPTION197 = "197"
    OPTION198 = "198"
    OPTION199 = "199"
    OPTION200 = "200"
    OPTION201 = "201"
    OPTION202 = "202"
    OPTION203 = "203"
    OPTION204 = "204"
    OPTION205 = "205"
    OPTION206 = "206"
    OPTION207 = "207"
    OPTION208 = "208"
    OPTION209 = "209"
    OPTION210 = "210"
    OPTION211 = "211"
    OPTION212 = "212"
    OPTION213 = "213"
    OPTION214 = "214"
    OPTION215 = "215"
    OPTION216 = "216"
    OPTION217 = "217"
    OPTION218 = "218"
    OPTION219 = "219"
    OPTION220 = "220"
    OPTION221 = "221"
    OPTION222 = "222"
    OPTION223 = "223"
    OPTION224 = "224"
    OPTION225 = "225"
    OPTION226 = "226"
    OPTION227 = "227"
    OPTION228 = "228"
    OPTION229 = "229"
    OPTION230 = "230"
    OPTION231 = "231"
    OPTION232 = "232"
    OPTION233 = "233"
    OPTION234 = "234"
    OPTION235 = "235"
    OPTION236 = "236"
    OPTION237 = "237"
    OPTION238 = "238"
    OPTION239 = "239"
    OPTION240 = "240"
    OPTION241 = "241"
    OPTION242 = "242"
    OPTION243 = "243"
    OPTION244 = "244"
    OPTION245 = "245"
    OPTION246 = "246"
    OPTION247 = "247"
    OPTION248 = "248"
    OPTION249 = "249"
    OPTION250 = "250"
    OPTION251 = "251"
    OPTION252 = "252"
    OPTION253 = "253"
    OPTION254 = "254"
    OPTION255 = "255"
    OPTION256 = "256"
    OPTION257 = "257"
    OPTION258 = "258"
    OPTION259 = "259"
    OPTION260 = "260"
    OPTION261 = "261"
    OPTION262 = "262"
    OPTION263 = "263"
    OPTION264 = "264"
    OPTION265 = "265"
    OPTION266 = "266"
    OPTION267 = "267"
    OPTION268 = "268"
    OPTION269 = "269"
    OPTION270 = "270"
    OPTION271 = "271"
    OPTION272 = "272"
    OPTION273 = "273"
    OPTION274 = "274"
    OPTION275 = "275"
    OPTION276 = "276"
    OPTION277 = "277"
    OPTION278 = "278"
    OPTION279 = "279"
    OPTION280 = "280"
    OPTION281 = "281"
    OPTION282 = "282"
    OPTION283 = "283"
    OPTION284 = "284"
    OPTION285 = "285"
    OPTION286 = "286"
    OPTION287 = "287"
    OPTION288 = "288"
    OPTION289 = "289"
    OPTION290 = "290"
    OPTION291 = "291"
    OPTION292 = "292"
    OPTION293 = "293"
    OPTION294 = "294"
    OPTION295 = "295"
    OPTION296 = "296"
    OPTION297 = "297"
    OPTION298 = "298"
    OPTION299 = "299"
    OPTION300 = "300"
    OPTION301 = "301"
    OPTION302 = "302"
    OPTION303 = "303"
    OPTION304 = "304"
    OPTION305 = "305"
    OPTION306 = "306"
    OPTION307 = "307"
    OPTION308 = "308"
    OPTION309 = "309"
    OPTION310 = "310"
    OPTION311 = "311"
    OPTION312 = "312"
    OPTION313 = "313"
    OPTION314 = "314"
    OPTION315 = "315"
    OPTION316 = "316"
    OPTION317 = "317"
    OPTION318 = "318"
    OPTION319 = "319"
    OPTION320 = "320"
    OPTION321 = "321"
    OPTION322 = "322"
    OPTION323 = "323"
    OPTION324 = "324"
    OPTION325 = "325"
    OPTION326 = "326"
    OPTION327 = "327"
    OPTION328 = "328"
    OPTION329 = "329"
    OPTION330 = "330"
    OPTION331 = "331"
    OPTION332 = "332"
    OPTION333 = "333"
    OPTION334 = "334"
    OPTION335 = "335"
    OPTION336 = "336"
    OPTION337 = "337"
    OPTION338 = "338"
    OPTION339 = "339"
    OPTION340 = "340"
    OPTION341 = "341"
    OPTION342 = "342"
    OPTION343 = "343"
    OPTION344 = "344"
    OPTION345 = "345"
    OPTION346 = "346"
    OPTION347 = "347"
    OPTION348 = "348"
    OPTION349 = "349"
    OPTION350 = "350"
    OPTION351 = "351"
    OPTION352 = "352"
    OPTION353 = "353"
    OPTION354 = "354"
    OPTION355 = "355"
    OPTION356 = "356"
    OPTION357 = "357"
    OPTION358 = "358"
    OPTION359 = "359"
    OPTION360 = "360"
    OPTION361 = "361"
    OPTION362 = "362"
    OPTION363 = "363"
    OPTION364 = "364"
    OPTION365 = "365"
    OPTION366 = "366"
    OPTION367 = "367"
    OPTION368 = "368"
    OPTION369 = "369"
    OPTION370 = "370"
    OPTION371 = "371"
    OPTION372 = "372"
    OPTION373 = "373"
    OPTION374 = "374"
    OPTION375 = "375"
    OPTION376 = "376"
    OPTION377 = "377"
    OPTION378 = "378"
    OPTION379 = "379"
    OPTION380 = "380"
    OPTION381 = "381"
    OPTION382 = "382"
    OPTION383 = "383"
    OPTION384 = "384"
    OPTION385 = "385"
    OPTION386 = "386"
    OPTION387 = "387"
    OPTION388 = "388"
    OPTION389 = "389"
    OPTION390 = "390"
    OPTION391 = "391"
    OPTION392 = "392"
    OPTION393 = "393"
    OPTION394 = "394"
    OPTION395 = "395"
    OPTION396 = "396"
    OPTION397 = "397"
    OPTION398 = "398"
    OPTION399 = "399"
    OPTION400 = "400"
    OPTION401 = "401"
    OPTION402 = "402"
    OPTION403 = "403"
    OPTION404 = "404"
    OPTION405 = "405"
    OPTION406 = "406"
    OPTION407 = "407"
    OPTION408 = "408"
    OPTION409 = "409"
    OPTION410 = "410"
    OPTION411 = "411"
    OPTION412 = "412"
    OPTION413 = "413"
    OPTION414 = "414"
    OPTION415 = "415"
    OPTION416 = "416"
    OPTION417 = "417"
    OPTION418 = "418"
    OPTION419 = "419"
    OPTION420 = "420"
    OPTION421 = "421"
    OPTION422 = "422"
    OPTION423 = "423"
    OPTION424 = "424"
    OPTION425 = "425"
    OPTION426 = "426"
    OPTION427 = "427"
    OPTION428 = "428"
    OPTION429 = "429"
    OPTION430 = "430"
    OPTION431 = "431"
    OPTION432 = "432"
    OPTION433 = "433"
    OPTION434 = "434"
    OPTION435 = "435"
    OPTION436 = "436"
    OPTION437 = "437"
    OPTION438 = "438"
    OPTION439 = "439"
    OPTION440 = "440"
    OPTION441 = "441"
    OPTION442 = "442"
    OPTION443 = "443"
    OPTION444 = "444"
    OPTION445 = "445"
    OPTION446 = "446"
    OPTION447 = "447"
    OPTION448 = "448"
    OPTION449 = "449"
    OPTION450 = "450"
    OPTION451 = "451"
    OPTION452 = "452"
    OPTION453 = "453"
    OPTION454 = "454"
    OPTION455 = "455"
    OPTION456 = "456"
    OPTION457 = "457"
    OPTION458 = "458"
    OPTION459 = "459"
    OPTION460 = "460"
    OPTION461 = "461"
    OPTION462 = "462"
    OPTION463 = "463"
    OPTION464 = "464"
    OPTION465 = "465"
    OPTION466 = "466"
    OPTION467 = "467"
    OPTION468 = "468"
    OPTION469 = "469"
    OPTION470 = "470"
    OPTION471 = "471"
    OPTION472 = "472"
    OPTION473 = "473"
    OPTION474 = "474"
    OPTION475 = "475"
    OPTION476 = "476"
    OPTION477 = "477"
    OPTION478 = "478"
    OPTION479 = "479"
    OPTION480 = "480"
    OPTION481 = "481"
    OPTION482 = "482"
    OPTION483 = "483"
    OPTION484 = "484"
    OPTION485 = "485"
    OPTION486 = "486"
    OPTION487 = "487"
    OPTION488 = "488"
    OPTION489 = "489"
    OPTION490 = "490"
    OPTION491 = "491"
    OPTION492 = "492"
    OPTION493 = "493"
    OPTION494 = "494"
    OPTION495 = "495"
    OPTION496 = "496"
    OPTION497 = "497"
    OPTION498 = "498"
    OPTION499 = "499"

def f(x: Intersection[Huge, Not[Literal[Huge.OPTION499]]]):
    # revealed: Literal[Huge.OPTION0, Huge.OPTION1, Huge.OPTION2, Huge.OPTION3, Huge.OPTION4, Huge.OPTION5, Huge.OPTION6, Huge.OPTION7, Huge.OPTION8, Huge.OPTION9, Huge.OPTION10, Huge.OPTION11, Huge.OPTION12, Huge.OPTION13, Huge.OPTION14, Huge.OPTION15, Huge.OPTION16, Huge.OPTION17, Huge.OPTION18, Huge.OPTION19, Huge.OPTION20, Huge.OPTION21, Huge.OPTION22, Huge.OPTION23, Huge.OPTION24, Huge.OPTION25, Huge.OPTION26, Huge.OPTION27, Huge.OPTION28, Huge.OPTION29, Huge.OPTION30, Huge.OPTION31, Huge.OPTION32, Huge.OPTION33, Huge.OPTION34, Huge.OPTION35, Huge.OPTION36, Huge.OPTION37, Huge.OPTION38, Huge.OPTION39, Huge.OPTION40, Huge.OPTION41, Huge.OPTION42, Huge.OPTION43, Huge.OPTION44, Huge.OPTION45, Huge.OPTION46, Huge.OPTION47, Huge.OPTION48, Huge.OPTION49, Huge.OPTION50, Huge.OPTION51, Huge.OPTION52, Huge.OPTION53, Huge.OPTION54, Huge.OPTION55, Huge.OPTION56, Huge.OPTION57, Huge.OPTION58, Huge.OPTION59, Huge.OPTION60, Huge.OPTION61, Huge.OPTION62, Huge.OPTION63, Huge.OPTION64, Huge.OPTION65, Huge.OPTION66, Huge.OPTION67, Huge.OPTION68, Huge.OPTION69, Huge.OPTION70, Huge.OPTION71, Huge.OPTION72, Huge.OPTION73, Huge.OPTION74, Huge.OPTION75, Huge.OPTION76, Huge.OPTION77, Huge.OPTION78, Huge.OPTION79, Huge.OPTION80, Huge.OPTION81, Huge.OPTION82, Huge.OPTION83, Huge.OPTION84, Huge.OPTION85, Huge.OPTION86, Huge.OPTION87, Huge.OPTION88, Huge.OPTION89, Huge.OPTION90, Huge.OPTION91, Huge.OPTION92, Huge.OPTION93, Huge.OPTION94, Huge.OPTION95, Huge.OPTION96, Huge.OPTION97, Huge.OPTION98, Huge.OPTION99, Huge.OPTION100, Huge.OPTION101, Huge.OPTION102, Huge.OPTION103, Huge.OPTION104, Huge.OPTION105, Huge.OPTION106, Huge.OPTION107, Huge.OPTION108, Huge.OPTION109, Huge.OPTION110, Huge.OPTION111, Huge.OPTION112, Huge.OPTION113, Huge.OPTION114, Huge.OPTION115, Huge.OPTION116, Huge.OPTION117, Huge.OPTION118, Huge.OPTION119, Huge.OPTION120, Huge.OPTION121, Huge.OPTION122, Huge.OPTION123, Huge.OPTION124, Huge.OPTION125, Huge.OPTION126, Huge.OPTION127, Huge.OPTION128, Huge.OPTION129, Huge.OPTION130, Huge.OPTION131, Huge.OPTION132, Huge.OPTION133, Huge.OPTION134, Huge.OPTION135, Huge.OPTION136, Huge.OPTION137, Huge.OPTION138, Huge.OPTION139, Huge.OPTION140, Huge.OPTION141, Huge.OPTION142, Huge.OPTION143, Huge.OPTION144, Huge.OPTION145, Huge.OPTION146, Huge.OPTION147, Huge.OPTION148, Huge.OPTION149, Huge.OPTION150, Huge.OPTION151, Huge.OPTION152, Huge.OPTION153, Huge.OPTION154, Huge.OPTION155, Huge.OPTION156, Huge.OPTION157, Huge.OPTION158, Huge.OPTION159, Huge.OPTION160, Huge.OPTION161, Huge.OPTION162, Huge.OPTION163, Huge.OPTION164, Huge.OPTION165, Huge.OPTION166, Huge.OPTION167, Huge.OPTION168, Huge.OPTION169, Huge.OPTION170, Huge.OPTION171, Huge.OPTION172, Huge.OPTION173, Huge.OPTION174, Huge.OPTION175, Huge.OPTION176, Huge.OPTION177, Huge.OPTION178, Huge.OPTION179, Huge.OPTION180, Huge.OPTION181, Huge.OPTION182, Huge.OPTION183, Huge.OPTION184, Huge.OPTION185, Huge.OPTION186, Huge.OPTION187, Huge.OPTION188, Huge.OPTION189, Huge.OPTION190, Huge.OPTION191, Huge.OPTION192, Huge.OPTION193, Huge.OPTION194, Huge.OPTION195, Huge.OPTION196, Huge.OPTION197, Huge.OPTION198, Huge.OPTION199, Huge.OPTION200, Huge.OPTION201, Huge.OPTION202, Huge.OPTION203, Huge.OPTION204, Huge.OPTION205, Huge.OPTION206, Huge.OPTION207, Huge.OPTION208, Huge.OPTION209, Huge.OPTION210, Huge.OPTION211, Huge.OPTION212, Huge.OPTION213, Huge.OPTION214, Huge.OPTION215, Huge.OPTION216, Huge.OPTION217, Huge.OPTION218, Huge.OPTION219, Huge.OPTION220, Huge.OPTION221, Huge.OPTION222, Huge.OPTION223, Huge.OPTION224, Huge.OPTION225, Huge.OPTION226, Huge.OPTION227, Huge.OPTION228, Huge.OPTION229, Huge.OPTION230, Huge.OPTION231, Huge.OPTION232, Huge.OPTION233, Huge.OPTION234, Huge.OPTION235, Huge.OPTION236, Huge.OPTION237, Huge.OPTION238, Huge.OPTION239, Huge.OPTION240, Huge.OPTION241, Huge.OPTION242, Huge.OPTION243, Huge.OPTION244, Huge.OPTION245, Huge.OPTION246, Huge.OPTION247, Huge.OPTION248, Huge.OPTION249, Huge.OPTION250, Huge.OPTION251, Huge.OPTION252, Huge.OPTION253, Huge.OPTION254, Huge.OPTION255, Huge.OPTION256, Huge.OPTION257, Huge.OPTION258, Huge.OPTION259, Huge.OPTION260, Huge.OPTION261, Huge.OPTION262, Huge.OPTION263, Huge.OPTION264, Huge.OPTION265, Huge.OPTION266, Huge.OPTION267, Huge.OPTION268, Huge.OPTION269, Huge.OPTION270, Huge.OPTION271, Huge.OPTION272, Huge.OPTION273, Huge.OPTION274, Huge.OPTION275, Huge.OPTION276, Huge.OPTION277, Huge.OPTION278, Huge.OPTION279, Huge.OPTION280, Huge.OPTION281, Huge.OPTION282, Huge.OPTION283, Huge.OPTION284, Huge.OPTION285, Huge.OPTION286, Huge.OPTION287, Huge.OPTION288, Huge.OPTION289, Huge.OPTION290, Huge.OPTION291, Huge.OPTION292, Huge.OPTION293, Huge.OPTION294, Huge.OPTION295, Huge.OPTION296, Huge.OPTION297, Huge.OPTION298, Huge.OPTION299, Huge.OPTION300, Huge.OPTION301, Huge.OPTION302, Huge.OPTION303, Huge.OPTION304, Huge.OPTION305, Huge.OPTION306, Huge.OPTION307, Huge.OPTION308, Huge.OPTION309, Huge.OPTION310, Huge.OPTION311, Huge.OPTION312, Huge.OPTION313, Huge.OPTION314, Huge.OPTION315, Huge.OPTION316, Huge.OPTION317, Huge.OPTION318, Huge.OPTION319, Huge.OPTION320, Huge.OPTION321, Huge.OPTION322, Huge.OPTION323, Huge.OPTION324, Huge.OPTION325, Huge.OPTION326, Huge.OPTION327, Huge.OPTION328, Huge.OPTION329, Huge.OPTION330, Huge.OPTION331, Huge.OPTION332, Huge.OPTION333, Huge.OPTION334, Huge.OPTION335, Huge.OPTION336, Huge.OPTION337, Huge.OPTION338, Huge.OPTION339, Huge.OPTION340, Huge.OPTION341, Huge.OPTION342, Huge.OPTION343, Huge.OPTION344, Huge.OPTION345, Huge.OPTION346, Huge.OPTION347, Huge.OPTION348, Huge.OPTION349, Huge.OPTION350, Huge.OPTION351, Huge.OPTION352, Huge.OPTION353, Huge.OPTION354, Huge.OPTION355, Huge.OPTION356, Huge.OPTION357, Huge.OPTION358, Huge.OPTION359, Huge.OPTION360, Huge.OPTION361, Huge.OPTION362, Huge.OPTION363, Huge.OPTION364, Huge.OPTION365, Huge.OPTION366, Huge.OPTION367, Huge.OPTION368, Huge.OPTION369, Huge.OPTION370, Huge.OPTION371, Huge.OPTION372, Huge.OPTION373, Huge.OPTION374, Huge.OPTION375, Huge.OPTION376, Huge.OPTION377, Huge.OPTION378, Huge.OPTION379, Huge.OPTION380, Huge.OPTION381, Huge.OPTION382, Huge.OPTION383, Huge.OPTION384, Huge.OPTION385, Huge.OPTION386, Huge.OPTION387, Huge.OPTION388, Huge.OPTION389, Huge.OPTION390, Huge.OPTION391, Huge.OPTION392, Huge.OPTION393, Huge.OPTION394, Huge.OPTION395, Huge.OPTION396, Huge.OPTION397, Huge.OPTION398, Huge.OPTION399, Huge.OPTION400, Huge.OPTION401, Huge.OPTION402, Huge.OPTION403, Huge.OPTION404, Huge.OPTION405, Huge.OPTION406, Huge.OPTION407, Huge.OPTION408, Huge.OPTION409, Huge.OPTION410, Huge.OPTION411, Huge.OPTION412, Huge.OPTION413, Huge.OPTION414, Huge.OPTION415, Huge.OPTION416, Huge.OPTION417, Huge.OPTION418, Huge.OPTION419, Huge.OPTION420, Huge.OPTION421, Huge.OPTION422, Huge.OPTION423, Huge.OPTION424, Huge.OPTION425, Huge.OPTION426, Huge.OPTION427, Huge.OPTION428, Huge.OPTION429, Huge.OPTION430, Huge.OPTION431, Huge.OPTION432, Huge.OPTION433, Huge.OPTION434, Huge.OPTION435, Huge.OPTION436, Huge.OPTION437, Huge.OPTION438, Huge.OPTION439, Huge.OPTION440, Huge.OPTION441, Huge.OPTION442, Huge.OPTION443, Huge.OPTION444, Huge.OPTION445, Huge.OPTION446, Huge.OPTION447, Huge.OPTION448, Huge.OPTION449, Huge.OPTION450, Huge.OPTION451, Huge.OPTION452, Huge.OPTION453, Huge.OPTION454, Huge.OPTION455, Huge.OPTION456, Huge.OPTION457, Huge.OPTION458, Huge.OPTION459, Huge.OPTION460, Huge.OPTION461, Huge.OPTION462, Huge.OPTION463, Huge.OPTION464, Huge.OPTION465, Huge.OPTION466, Huge.OPTION467, Huge.OPTION468, Huge.OPTION469, Huge.OPTION470, Huge.OPTION471, Huge.OPTION472, Huge.OPTION473, Huge.OPTION474, Huge.OPTION475, Huge.OPTION476, Huge.OPTION477, Huge.OPTION478, Huge.OPTION479, Huge.OPTION480, Huge.OPTION481, Huge.OPTION482, Huge.OPTION483, Huge.OPTION484, Huge.OPTION485, Huge.OPTION486, Huge.OPTION487, Huge.OPTION488, Huge.OPTION489, Huge.OPTION490, Huge.OPTION491, Huge.OPTION492, Huge.OPTION493, Huge.OPTION494, Huge.OPTION495, Huge.OPTION496, Huge.OPTION497, Huge.OPTION498]
    reveal_type(x)

Simplifying gradually-equivalent types

If two types are gradually equivalent, we can keep just one of them in a union:

from typing import Any, Union
from ty_extensions import Intersection, Not

def _(x: Union[Intersection[Any, Not[int]], Intersection[Any, Not[int]]]):
    reveal_type(x)  # revealed: Any & ~int

Bidirectional Type Inference

[environment]
python-version = "3.12"

Type inference accounts for parameter type annotations across all signatures in a union.

from typing import TypedDict, overload

class T(TypedDict):
    x: int

def _(flag: bool):
    if flag:
        def f(x: T) -> int:
            return 1
    else:
        def f(x: dict[str, int]) -> int:
            return 1
    x = f({"x": 1})
    reveal_type(x)  # revealed: int

    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `T`, found `dict[str, int] & dict[Unknown | str, Unknown | int]`"
    f({"y": 1})