mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:35:58 +00:00

You can now use subscript expressions in a type expression to explicitly specialize generic classes, just like you could already do in value expressions. This still does not implement bidirectional checking, so a type annotation on an assignment does not influence how we infer a specialization for a (not explicitly specialized) constructor call. You might get an `invalid-assignment` error if (a) we cannot infer a class specialization from the constructor call (in which case you end up e.g. trying to assign `C[Unknown]` to `C[int]`) or if (b) we can infer a specialization, but it doesn't match the annotation. Closes https://github.com/astral-sh/ruff/issues/17432
2.2 KiB
2.2 KiB
cast
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 red-knot, they are understood as different types by
human readers of red-knot'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.
from knot_extensions import Unknown
def f(x: Any, y: Unknown, z: Any | str | int):
a = cast(dict[str, Any], x)
reveal_type(a) # revealed: @Todo(specialized non-generic class)
b = cast(Any, y)
reveal_type(b) # revealed: Any
c = cast(str | int | Any, z) # error: [redundant-cast]