Sitemap

Type Hinting in Python

3 min readDec 16, 2025

--

Press enter or click to view image in full size
Photo by James Harrison on Unsplash

You can create Python functions with expected types for arguments and returned values using Type Hinting (introduced in Python 3.5 and enhanced in later versions).

Get Adhi Setyatuhu’s stories in your inbox

Join Medium for free to get updates from this writer.

Type hinting does not enforce types at runtime (Python remains dynamically typed), but it provides several critical benefits for code quality and maintainability:

  1. Readability: It makes the function’s contract immediately clear to other developers.
  2. Tooling & Refactoring: It enables powerful static type checkers (like Mypy, Pyright) and IDEs (like VS Code, PyCharm) to catch type-related bugs before the code is run.
  3. Documentation: It serves as a concise, live form of documentation.

Here is how you implement it, using the standard syntax and key modules.

1. Basic Syntax

The syntax uses a colon (:) after the argument name to indicate its type, and an arrow (->) before the final colon of the function signature to indicate the return type.

def add_numbers(a: int, b: int) -> int:
"""
A simple function with explicit type hints.
Expects two integers and returns one integer.
"""
return a + b
# Usage:
result = add_numbers(5, 3) # result is 8 (int)
# If you passed a float, the type checker would flag it:
# result_bad = add_numbers(5.0, 3)

2. Using the typing Module for Complex Types

For types that aren’t simple built-ins (like int, str, list), you need to import specific objects from the built-in typing module.

A. Collections (Lists, Dictionaries, Tuples, Sets)

You need to specify the type of the elements inside the collection using square brackets.

from typing import List, Dict, Tuple, Set
def process_data(user_list: List[str], config: Dict[str, bool]) -> Set[str]:
"""
Expects a list containing strings, and a dictionary
where keys are strings and values are booleans.
Returns a set containing strings.
"""
processed_names = {name.upper() for name in user_list}

if config.get('sort_output'):
# Note: Sets can't be sorted directly, but this illustrates the type contract.
return processed_names

return processed_names


# Example with specific type structure:
def get_coordinates() -> Tuple[int, int, int]:
return (10, 20, 30)

B. Optional and Union Types

When an argument or return value can be one of several types, or if it can be None, use Union or Optional.

Press enter or click to view image in full size
from typing import Optional, Union
# Python < 3.10 syntax
def find_item(data: List[str], name: str) -> Optional[str]:
"""Returns a string if found, otherwise returns None."""
if name in data:
return name
return None


# Python 3.10+ syntax (more concise)
def handle_input(value: int | str) -> Union[int, float]:
"""Accepts either an int or a string, returns an int or a float."""
if isinstance(value, int):
return value + 1
return len(value) / 2.0

C. Callable (Functions as Arguments)

Use Callable to specify a function that is expected as an argument.

from typing import Callable, Any
# Callable[[Arg1_Type, Arg2_Type, ...], Return_Type]
def apply_function(data: List[int], func: Callable[[int], int]) -> List[int]:
"""
Takes a list of integers and a function that accepts an int and returns an int.
"""
return [func(x) for x in data]

def square(x: int) -> int:
return x * x


# Usage:
result = apply_function([1, 2, 3], square) # result is [1, 4, 9]

By the way, if you’re into AI agents, I’ve been actively exploring Agentic AI over on my other blog, https://adhisetyatuhu.my.id. Feel free to check it out.

--

--

Adhi Setyatuhu

Written by Adhi Setyatuhu

Software dev by trade (JS/Python/Django), AI agent tinkerer on the side. I write about the stuff I build and break along the way.