crates/ty_python_semantic/resources/mdtest/function/parameters.md
Within a function scope, the declared type of each parameter is its annotated type (or Unknown if
not annotated). The initial inferred type is the annotated type of the parameter, if any. If there
is no annotation, it is the union of Unknown with the type of the default value expression (if
any).
The variadic parameter is a variadic tuple of its annotated type; the variadic-keywords parameter is a dictionary from strings to its annotated type.
from typing import Literal
def f(a, b: int, c=1, d: int = 2, /, e=3, f: Literal[4] = 4, *args: object, g=5, h: Literal[6] = 6, **kwargs: str):
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: int
reveal_type(c) # revealed: Unknown | Literal[1]
reveal_type(d) # revealed: int
reveal_type(e) # revealed: Unknown | Literal[3]
reveal_type(f) # revealed: Literal[4]
reveal_type(g) # revealed: Unknown | Literal[5]
reveal_type(h) # revealed: Literal[6]
reveal_type(args) # revealed: tuple[object, ...]
reveal_type(kwargs) # revealed: dict[str, str]
...are inferred as tuple of Unknown or dict from string to Unknown.
def g(*args, **kwargs):
reveal_type(args) # revealed: tuple[Unknown, ...]
reveal_type(kwargs) # revealed: dict[str, Unknown]
If there is an annotation, we respect it fully and don't union in the default value type.
from typing import Any
def f(x: Any = 1):
reveal_type(x) # revealed: Any
The default value type must be assignable to the annotated type. If not, we emit a diagnostic, and fall back to inferring the annotated type, ignoring the default value type.
# error: [invalid-parameter-default]
def f(x: int = "foo"):
reveal_type(x) # revealed: int
# The check is assignable-to, not subtype-of, so this is fine:
from typing import Any
def g(x: Any = "foo"):
reveal_type(x) # revealed: Any
from typing import TypedDict
class Foo(TypedDict):
x: int
def x(a: Foo = {"x": 42}): ...
def y(a: Foo = dict(x=42)): ...
from typing import TypedDict
class Foo(TypedDict):
x: int
y: int
# error: [missing-typed-dict-key]
def missing_key(a: Foo = {"x": 42}): ...
# error: [invalid-argument-type]
def wrong_type(a: Foo = {"x": "s", "y": 1}): ...
# error: [invalid-key]
def extra_key(a: Foo = {"x": 1, "y": 2, "z": 3}): ...
[environment]
python-version = "3.12"
from typing import Protocol
class Foo(Protocol):
def x(self, y: bool = ...): ...
def y[T](self, y: T = ...) -> T: ...
class GenericFoo[T](Protocol):
def x(self, y: bool = ...) -> T: ...
from abc import abstractmethod
class Bar:
@abstractmethod
def x(self, y: bool = ...): ...
@abstractmethod
def y[T](self, y: T = ...) -> T: ...
from typing import overload
@overload
def x(y: None = ...) -> None: ...
@overload
def x(y: int) -> str: ...
def x(y: int | None = None) -> str | None: ...
if TYPE_CHECKING blocksWe generally view code in if TYPE_CHECKING blocks as having the same semantics and exemptions to
code in stub files:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
def foo(x: bool = ...): ... # fine