Basics
The most basic type hints are variable annotations. These will cause the type checker to raise an error when an incompatible type is assigned:
answer: int = 42
answer = 54
answer = "42" #> str is incompatible with int
We don't need to declare the variable to type it:
answer: int
answer = 42
answer = "42" #> str is incompatible with int
Inference
Usually, we don't need to type variables explicitly. The type checker can often figure out type of the variable from its assignment, this is called inference.
When we assign answer
to 42
, the type checker can infer that the type of answer must be int
. This inference also works with the common practice of reassigning variables with a value of a different type. The type checker will track the type of the variable after each assignment, and it will apply type checking rules based on that type:
answer = 42
answer += 8
# A new type `str` is inferred for answer, since it did not have a type hint
answer = "forty"
answer += " two"
answer += 8 #> Operator "+" not supported for types str and int
Subtypes
The type of a value does not need to exactly match the variable's type, whether inferred or explicit. The type checker will also accept values which are a subtype of the variable's type.
For example, UserId
is a subclass (and so, subtype) of int
. It can be assigned to a variable annotated int
:
class UserID(int):
pass
user_id: int = UserID(42)
We'll look at this in depth later, in the Subtypes section.