Q1Which is the correct way to annotate def add(x, y): so both arguments and the return value are int?
Type Hints — Leave Your Intent in the Code with : int and -> str
Learn Python type hints. The def f(x: int) -> str: function signature, when to add variable annotations and when not to, and how IDEs use them for static checks — hands-on.
This article covers type hints — a tool for leaving "what goes here" intent inside the code. Introduced in Python 3.5, they've grown more expressive with each release.
The "What's the Type?" Problem in a Dynamic Language
Python is a dynamically typed language. Write value = 10 and it's int; write value = "HELLO" and the same name is now str. The same variable can flip type at any time — that's flexibility. The downside is that as your codebase grows or a team starts touching it, "what does this variable hold?" / "what does this function return?" becomes unanswerable from the code alone.
Type hints leave that information as annotations. They don't change runtime behavior. What they do is give human readers a strong hint and let IDEs and static analyzers catch type mistakes ahead of time.
price should be. With hints, the function signature alone tells you the input and output types.Annotating Function Signatures
Function signatures are the main stage for type hints. Annotate arguments as name: type (colon then type after the name) and the return type as -> type (arrow then type at the end of the def line).
# ❌ no hints — the signature doesn't tell you what to pass
def calc_tax(price, rate):
return int(price * (1 + rate))
# ✅ with hints — "int in, int out" is obvious
def calc_tax(price: int, rate: float) -> int:
return int(price * (1 + rate))
print(calc_tax(price=1000, rate=0.1)) # 1100
Variable Annotations — When to Write Them and When Not To
Type hints work on variables too — same shape: name: type = value. But variable annotations get noisy fast if you overuse them, and noise reduces readability.
# ❌ noisy — the value already tells you the type
name: str = "Alice"
age: int = 30
# ✅ worth annotating — empty container, deferred init, or unclear type
results: list = [] # empty list, will append later
cache: dict = {} # may want to detail value type later
user_id: int = fetch_id_from_session() # makes the call site's expected type explicit
When in doubt: "functions always, variables sparingly"
In real projects, always annotate function and method signatures, but annotate variables only when the type isn't obvious. age = 30 doesn't need : int — adding it is just noise.
Type Hints Are Not Enforced at Runtime
Type hints are just hints. Python does not check or enforce them at runtime. A function declared def f(x: int) happily accepts f("abc") — the function runs as usual. Only later, when a calculation deep inside the body fails on the type mismatch, do you see a TypeError or similar — and that's a runtime error from the calculation, not from the hint itself.
So why bother? Because before the function ever runs, editors and static analyzers can detect type mismatches. VS Code's Pylance, PyCharm, mypy, pyright — all of them read your hints and underline mistakes in red, catching bugs before execution.
Browser Python runners don't show IDE warnings
The console on this page only runs your code — it has no static type-checking facility like VS Code does. So writing or violating type hints never produces red underlines or pre-run warnings here. The real benefit of type hints — the safety net of editor warnings before you run — only shows up locally with VS Code, PyCharm, etc.
Three Places Type Hints Pay Off
Knowledge Check
Answer each question one by one.
Q2Which is the most accurate statement about Python type hints?
Q3Which is the most recommended place to add type hints?