Skip to content

Callable and TypeAlias

Callable annotates functions or callable objects, while TypeAlias creates named type aliases for complex type annotations.

Mental Model

Callable[[ArgTypes], ReturnType] is the type hint for "anything you can call like a function." Use it to annotate callbacks, strategy parameters, or decorator arguments. TypeAlias gives a name to a long or complex type expression so you can reuse it without repeating the full definition — like a variable for types.

Callable - Function Type Hints

Use Callable to annotate functions and callbacks.

```python from typing import Callable

Function that takes a Callable

def apply_operation(a: int, b: int, op: Callable[[int, int], int]) -> int: return op(a, b)

Callable with different signatures

def transform(data: list[str], processor: Callable[[str], int]) -> list[int]: return [processor(item) for item in data]

result1 = apply_operation(5, 3, lambda x, y: x + y) result2 = transform(["hello", "world"], len)

print(result1) print(result2) ```

8 [5, 5]

TypeAlias - Named Type Aliases

Create reusable type definitions with TypeAlias.

```python from typing import TypeAlias

Create named aliases for complex types

UserId: TypeAlias = int UserName: TypeAlias = str UserData: TypeAlias = dict[UserId, UserName]

def get_user_name(users: UserData, user_id: UserId) -> UserName | None: return users.get(user_id)

users: UserData = {1: "Alice", 2: "Bob"} print(get_user_name(users, 1)) print(get_user_name(users, 99)) ```

Alice None


Exercises

Exercise 1. Write a type alias Predicate for a function that takes an int and returns a bool. Then write a function filter_numbers(numbers: list[int], pred: Predicate) -> list[int] that filters a list using the predicate.

Solution to Exercise 1

```python from typing import Callable, TypeAlias

Predicate: TypeAlias = Callable[[int], bool]

def filter_numbers(numbers: list[int], pred: Predicate) -> list[int]: return [n for n in numbers if pred(n)]

evens = filter_numbers([1, 2, 3, 4, 5], lambda x: x % 2 == 0) print(evens) # [2, 4] ```


Exercise 2. Annotate a callback parameter using Callable. Write a function apply_twice(func: Callable[[int], int], value: int) -> int that applies func to value twice.

Solution to Exercise 2

```python from typing import Callable

def apply_twice(func: Callable[[int], int], value: int) -> int: return func(func(value))

print(apply_twice(lambda x: x + 1, 5)) # 7 print(apply_twice(lambda x: x * 2, 3)) # 12 ```


Exercise 3. Create a TypeAlias called Matrix for list[list[float]]. Write a function transpose(m: Matrix) -> Matrix with proper annotations.

Solution to Exercise 3

```python from typing import TypeAlias

Matrix: TypeAlias = list[list[float]]

def transpose(m: Matrix) -> Matrix: rows = len(m) cols = len(m[0]) return [[m[r][c] for r in range(rows)] for c in range(cols)]

mat: Matrix = [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]] print(transpose(mat)) # [[1.0, 3.0, 5.0], [2.0, 4.0, 6.0]] ```


Exercise 4. Explain the difference between Callable[[int, str], bool] and Callable[..., bool]. When would you use each?

Solution to Exercise 4

Callable[[int, str], bool] specifies a function that takes exactly an int and a str and returns a bool. Callable[..., bool] specifies a function that returns bool but accepts any number and types of arguments.

Use the first form when you know the exact signature of the callback. Use ... when the function signature varies or you want maximum flexibility, such as when wrapping arbitrary functions in a decorator.