Python by Contrast
Syntax & types by contrast
Lesson 1 of 5
What you'll learn
- Read indentation-as-syntax without reaching for braces
- Understand dynamic typing plus optional type hints
- See where mypy and pyright sit relative to tsc
Python is dynamically typed and interpreted — the opposite corner from TypeScript on both axes. There is no compile step that proves your types, and blocks are delimited by indentation, not braces. The payoff is code that reads close to pseudocode; the cost is that correctness checking moves into optional tooling.
def classify(age: int) -> str:
if age >= 18:
return "adult"
return "minor"
print(classify(36)) # adult
The same function in TypeScript:
function classify(age: number): string {
if (age >= 18) {
return "adult";
}
return "minor";
}
Indentation is the syntax
A colon opens a block and a consistent indent (four spaces, by convention) defines its extent. There are no braces to mismatch and no semicolons to argue about. Get the indent wrong and it isn't a style nit — it's an IndentationError, or worse, a statement that silently fell out of the block above it.
Hints, not a compiler
The annotations above — age: int, -> str — are type hints. Modern Python uses the builtin generics directly: list[int], dict[str, int], and str | None for optionals. But hints are metadata: the interpreter stores them and otherwise ignores them. classify("36") runs without complaint.
Checking hints is the job of a separate static checker — mypy (the reference implementation) or pyright (the fast one that powers Pylance in VS Code). Think of it as tsc --noEmit unbundled from your build: TS types also erase at runtime, but tsc blocks a broken build by default, while Python's checker only gates what you wire it to — usually CI.
Pick pyright, run it in CI
For a TS developer, pyright feels most like home: fast, strict-mode flags, great editor integration. Whichever checker you choose, a hint nobody checks is just a comment — put the checker in CI on day one.
The challenge is a JS model of gradual typing: hints ride along as plain data, a "checker" pass reads them, and the call runs anyway.
Run it. This models Python's split: a checker flags the bad call, but the runtime executes it anyway.
What happens at runtime when a Python type hint is violated?
Next: why virtualenvs exist at all — and how uv turns Python packaging into one fast tool.
Saved on this device. Sign in to sync your progress everywhere.