
Closes https://github.com/astral-sh/ty/issues/209. Before: ``` error[static-assert-error]: Static assertion error: custom message --> test.py:2:1 | 1 | from ty_extensions import static_assert 2 | static_assert(3 > 4, "custom message") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ``` After: ``` error[static-assert-error]: Static assertion error: custom message --> test.py:2:1 | 1 | from ty_extensions import static_assert 2 | static_assert(3 > 4, "custom message") | ^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^ | | | Inferred type of argument is `Literal[False]` | ```
2.8 KiB
cast
Behavior
cast()
takes two arguments, one type and one value, and returns a value of the given type.
The (inferred) type of the value and the given type do not need to have any correlation.
from typing import Literal, cast, Any
reveal_type(True) # revealed: Literal[True]
reveal_type(cast(str, True)) # revealed: str
reveal_type(cast("str", True)) # revealed: str
reveal_type(cast(int | str, 1)) # revealed: int | str
reveal_type(cast(val="foo", typ=int)) # revealed: int
# error: [invalid-type-form]
reveal_type(cast(Literal, True)) # revealed: Unknown
# error: [invalid-type-form]
reveal_type(cast(1, True)) # revealed: Unknown
# error: [missing-argument] "No argument provided for required parameter `val` of function `cast`"
cast(str)
# error: [too-many-positional-arguments] "Too many positional arguments to function `cast`: expected 2, got 3"
cast(str, b"ar", "foo")
def function_returning_int() -> int:
return 10
# error: [redundant-cast] "Value is already of type `int`"
cast(int, function_returning_int())
def function_returning_any() -> Any:
return "blah"
# error: [redundant-cast] "Value is already of type `Any`"
cast(Any, function_returning_any())
Complex type expressions (which may be unsupported) do not lead to spurious [redundant-cast]
diagnostics.
from typing import Callable
def f(x: Callable[[dict[str, int]], None], y: tuple[dict[str, int]]):
a = cast(Callable[[list[bytes]], None], x)
b = cast(tuple[list[bytes]], y)
A cast from Todo
or Unknown
to Any
is not considered a "redundant cast": even if these are
understood as gradually equivalent types by ty, they are understood as different types by human
readers of ty's output. For Unknown
in particular, we may consider it differently in the context
of some opt-in diagnostics, as it indicates that the gradual type has come about due to an invalid
annotation, missing annotation or missing type argument somewhere.
A cast from Unknown
to Todo
or Any
is also not considered a "redundant cast", as this breaks
the gradual guarantee and leads to cascading errors when an object is inferred as having type
Unknown
due to a missing import or similar.
from ty_extensions import Unknown
def f(x: Any, y: Unknown, z: Any | str | int):
a = cast(dict[str, Any], x)
reveal_type(a) # revealed: dict[str, Any]
b = cast(Any, y)
reveal_type(b) # revealed: Any
c = cast(Unknown, y)
reveal_type(c) # revealed: Unknown
d = cast(Unknown, x)
reveal_type(d) # revealed: Unknown
e = cast(str | int | Any, z) # error: [redundant-cast]
Diagnostic snapshots
import secrets
from typing import cast
# error: [redundant-cast] "Value is already of type `int`"
cast(int, secrets.randbelow(10))