Skip to lesson
Python for JS Developers

Modern PythonLesson 2 of 3

Type Hints

3 min

Python's type hints look a lot like TypeScript annotations, with one crucial difference: Python does not enforce them at runtime. They are checked by separate tools (mypy, pyright, or your editor) before you run the code, and they double as documentation. Code with wrong hints still runs.

Use the built-in types directly: name: str, scores: list[int], ages: dict[str, int], point: tuple[float, float]. Since Python 3.9 you no longer need typing.List or typing.Dict, and since 3.10 unions are written with |: str | int.

Careful with "optional". In TypeScript, title?: string means the parameter can be left out. In Python, title: str | None only says the value may be None; the caller must still pass it unless you also give it a default: title: str | None = None. The old spelling Optional[str] means exactly str | None, which is why the name confuses people.

Protocol classes are Python's answer to TypeScript interfaces. Any class that has the right methods satisfies the Protocol without inheriting from it. This is structural typing, the same model TypeScript uses.

Python 3.12 added a cleaner syntax for generics: def first[T](items: list[T]) -> T, and type aliases with the type statement: type UserId = int | str.

Quick reference

JavaScriptPythonNote
x: stringx: strBuilt-in type names differ
number[]list[int] / list[float]Built-in generics (3.9+)
string | numberstr | intUnion with | (3.10+)
title?: stringtitle: str | None = NoneNeeds a default to be omittable
interface Drawable { }class Drawable(Protocol):Structural typing
(a: T) => RCallable[[T], R]from collections.abc
function first<T>(xs: T[]): Tdef first[T](xs: list[T]) -> T:Generics syntax (3.12+)
tsc --noEmitmypy / pyrightNot enforced at runtime

Side by side

JavaScript
// TypeScript
let name: string = "Alice";
let scores: number[] = [1, 2, 3];
let id: string | number;

function greet(name: string, title?: string): string {
  return title ? title + " " + name : name;
}

interface Drawable {
  draw(): string;
}
Python
# Python type hints
from collections.abc import Callable
from typing import Protocol

name: str = "Alice"
scores: list[int] = [1, 2, 3]
id_val: str | int = "abc"

def greet(name: str, title: str | None = None) -> str:
    return f"{title} {name}" if title else name

# Protocol = structural typing (like a TS interface)
class Drawable(Protocol):
    def draw(self) -> str: ...

class Circle:              # no inheritance needed
    def draw(self) -> str:
        return "()"

def render(shape: Drawable) -> None:
    print(shape.draw())

render(Circle())           # OK for the type checker

# Callable type
def apply(fn: Callable[[int, int], int], a: int, b: int) -> int:
    return fn(a, b)

# Generic function (3.12+)
def first[T](items: list[T]) -> T:
    return items[0]

Exercise

Real Python, running in your browser

Add type hints to every parameter and return value:

Starter code
def process(items, transformer, default_val):
    results = []
    for item in items:
        try:
            results.append(transformer(item))
        except Exception:
            results.append(default_val)
    return results

def find_user(users, user_id):
    for user in users:
        if user["id"] == user_id:
            return user
    return None

Ctrl + Enter runCtrl + Shift + Enter checkTab indentEsc then Tab to leave the editorCtrl + / comment