Type Annotations in Python


As a TypeScript developer, you're likely familiar with the benefits of a robust type system. However, Python's dynamic typing may seem like a departure from the strict type safety you're used to. But fear not! Python's introduction of type hints (PEP 484) has brought gradual typing to the table, offering the best of both worlds.
At first glance, Python's type annotations might seem to contradict its dynamic nature, which is a key strength for rapid development and beginner-friendly coding. Yet, these annotations provide significant advantages in modern software development. As projects scale, type hints enable:
- Early detection of type-related errors
- Streamlined and reliable code refactoring
- Reduced need for extensive validation tests
- Immediate clarity on function parameters and return values
These benefits ultimately enhance code readability, making it more self-documenting and maintainable.
In this guide, we’ll explore how type annotations work in Python, comparing them with TypeScript where relevant.
- Basic Type Annotations
In TypeScript, you might write:
function greet(name: string): string { return `Hello, ${name}!`;}
The Python equivalent using type hints is:
def greet(name: str) -> str: return f"Hello, {name}!"
The syntax is straightforward: name: str indicates that name should be a string, and -> str specifies that the function returns a string.
2. Primitive Types
Python provides type hints for primitive types, much like TypeScript:
TypeScript Python
string str
number int, float
boolean bool
null None
Example:
def add(a: int, b: int) -> int: return a + b
Unlike TypeScript, Python does not have a dedicated number type; instead, it differentiates between int and float.
3. Lists, Tuples, and Dictionaries
Python uses list, tuple, and dict generics for collections:
def process_items(items: list[str]) -> None: for item in items: print(item)
For tuples:
def get_point() -> tuple[int, int]: return (10, 20)
For dictionaries:
def user_info() -> dict[str, int]: return {"age": 30, "score": 100}
This is somewhat similar to TypeScript:
let userInfo: Record<string, number> = { age: 30, score: 100 };
4. Optional and Union Types
Python uses Optional and Union from typing:
from typing import Optional, Union def fetch_data(id: int) -> Optional[str]: if id == 1: return "Data" return None
Equivalent to TypeScript:
function fetchData(id: number): string | null {return id === 1 ? "Data" : null;}
Since Python 3.10, you can use | for union types:
def process(value: int | str) -> None: print(value)
5. Custom Types and Type Aliases
Like TypeScript’s type aliases, Python supports TypeAlias:
from typing import TypeAlias UserID: TypeAlias = int def get_user(id: UserID) -> str: return f"User {id}"
Equivalent to:
type UserID = number; function getUser(id: UserID): string {return `User ${id}`;}
6. Classes and Typed Attributes
Python supports type hints for class attributes:
class Person: name: str age: int def __init__(self, name: str, age: int) -> None: self.name = name self.age = age
Similar to TypeScript:
class Person {name: string; age: number; constructor(name: string, age: number) {this.name = name; this.age = age;} }
7. Generics in Python
Python supports generics using TypeVar:
from typing import TypeVar, Generic T = TypeVar("T") class Box(Generic[T]): def __init__(self, value: T): self.value = value def get_value(self) -> T: return self.value
Equivalent TypeScript:
class Box<T> {constructor(private value: T) {}getValue(): T {return this.value;} }
8. Structural Typing with Protocols
Python supports duck typing via Protocol:
from typing import Protocol class HasName(Protocol): name: str class User: def __init__(self, name: str): self.name = name def greet(entity: HasName) -> str: return f"Hello, {entity.name}!"
Equivalent to TypeScript interfaces:
interface HasName {name: string;} class User {constructor(public name: string) {}} function greet(entity: HasName): string {return `Hello, ${entity.name}!`;}
9. Enforcing Types with mypy
Unlike TypeScript, Python does not enforce type safety at runtime. However, you can use mypy to perform static type checking:
pip install mypy mypy my_script.py
If your code has type issues, mypy will catch them before runtime.
If you're using Visual Studio Code, Pyright is integrated via the Pylance extension. Simply enable Pylance to benefit from real-time type checking.
To configure strict type checking, add the following to your pyproject.toml:
<<toml>>
[tool.pyright]
typeCheckingMode = "strict" # Options: "off", "basic", "strict"
<<>>
Type annotations in Python bring many TypeScript-like benefits.
Subscribe to my newsletter
Read articles from Ayomikun Osota directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
