Last modified: Sep 10, 2026

Make Python Statically Typed

Python is a dynamically typed language. This means types are checked at runtime. However, you can add static typing using type hints. Static typing helps catch errors before running the code.

Why Use Static Typing?

Static typing improves code readability. It makes large projects easier to maintain. IDEs can give better autocomplete suggestions. Tools like mypy can check types without executing the code.

Adding Type Hints to Variables

You can annotate variables with their expected types. This does not change how Python runs the code. It only adds metadata.


# Annotating a string variable
name: str = "Alice"

# Annotating an integer
age: int = 30

# Annotating a boolean
is_student: bool = False

No output is shown because these are just annotations. They help developers and tools understand the intended types.

Type Hints in Functions

Functions can also have type hints. You specify the parameter types and the return type.


def greet(name: str) -> str:
    return f"Hello, {name}!"

message = greet("Bob")
print(message)

Hello, Bob!

Here, greet takes a string and returns a string. If you pass the wrong type, tools like mypy will warn you.

Using Complex Types

You can use complex types like lists and dictionaries. Import the List and Dict types from the typing module.


from typing import List, Dict

def process_items(items: List[str]) -> Dict[str, int]:
    counts = {}
    for item in items:
        counts[item] = len(item)
    return counts

result = process_items(["apple", "banana"])
print(result)

{'apple': 5, 'banana': 6}

This function takes a list of strings and returns a dictionary mapping each string to its length.

Optional and Union Types

Sometimes a variable can have more than one type. Use Union to specify multiple possible types.


from typing import Union

def parse_value(value: Union[str, int]) -> str:
    return str(value)

print(parse_value("123"))
print(parse_value(456))

123
456

You can also use Optional when a value might be None.


from typing import Optional

def get_name(name: Optional[str]) -> str:
    if name is None:
        return "Unknown"
    return name

print(get_name(None))
print(get_name("Alice"))

Unknown
Alice

Checking Types with mypy

To enforce static typing, use a type checker like mypy. Install it using pip.


pip install mypy

Run mypy on your Python file to check for type errors.


mypy my_script.py

If there are type mismatches, mypy will report them.

Classes and Static Typing

You can add type hints to class attributes and methods.


class Person:
    name: str
    age: int

    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age

    def introduce(self) -> str:
        return f"My name is {self.name} and I am {self.age} years old."

person = Person("Alice", 30)
print(person.introduce())

My name is Alice and I am 30 years old.

Using Type Aliases

For readability, you can create type aliases for complex types.


from typing import List

# Creating a type alias
Coordinate = List[float]

def distance(point_a: Coordinate, point_b: Coordinate) -> float:
    return ((point_a[0] - point_b[0])**2 + (point_a[1] - point_b[1])**2)**0.5

print(distance([0.0, 0.0], [3.0, 4.0]))

5.0

Generics with Type Variables

Use TypeVar for generic functions that work with multiple types.


from typing import TypeVar, List

T = TypeVar('T')

def first_item(items: List[T]) -> T:
    return items[0]

print(first_item([1, 2, 3]))
print(first_item(["a", "b", "c"]))

1
a

Conclusion

Adding static typing to Python enhances code quality and developer experience. Type hints provide clarity and enable powerful tooling. Use mypy to catch type errors early. While Python remains dynamically typed at runtime, static typing gives you the best of both worlds.

Start small by annotating function signatures. Gradually add more annotations as you become comfortable. This improves maintainability and reduces bugs in large codebases.