Skip to main content

Overview

Typer is a library for building CLI applications that users will love using and developers will love creating. It leverages Python type hints to define the interface of your command-line tools, providing automatic validation, serialization, and beautiful help documentation with minimal boilerplate.

Built on top of Click, Typer provides a more modern, type-safe API while remaining compatible with the vast Click ecosystem.

The Problem

Creating command-line interfaces in Python often involves a trade-off:

  1. Manual parsing (using sys.argv): Error-prone, tedious, and lacks documentation.
  2. Standard libraries (like argparse): Verbose, requires repetitive definitions of types and help strings, and doesn't leverage modern Python type safety.
  3. Complex frameworks: High learning curve and often separate the CLI definition from the actual logic.

Typer solves this by making your code the definition. By using type hints, you define your logic and your CLI interface simultaneously.

Core Mental Model: Your Function is Your CLI

In Typer, a CLI command is just a regular Python function.

  • Function Name: Becomes the command name.
  • Parameters: Become CLI arguments or options.
  • Type Hints: Handle data conversion (e.g., string to int or Path) and validation.
  • Docstrings: Automatically become the help documentation.

Think of Typer as a "translator" that maps terminal input directly to your function's arguments.

How It Works

  1. Declaration: you create a typer.Typer instance or use typer.run().
  2. Registration: you decorate functions with @app.command() to register them as subcommands.
  3. Configuration: you use typing.Annotated with typer.Argument or typer.Option to add metadata like help text or environment variable overrides.
  4. Execution: when the app runs, Typer parses the command line, validates the inputs against your type hints, and calls your function with the parsed values.
  5. Rich Output: if Rich Help Formatting is installed, Typer automatically formats help screens and error messages with colors and panels.

Use Cases

Simple One-Command Script

For quick scripts, typer.run turns a function into a CLI immediately.

import typer

def main(name: str, count: int = 1):
for _ in range(count):
print(f"Hello {name}")

if __name__ == "__main__":
typer.run(main)

Multi-Command Application

Use the Typer class to build complex tools with subcommands.

import typer

app = typer.Typer()

@app.command()
def create(username: str):
print(f"Creating user: {username}")

@app.command()
def delete(username: str, force: bool = False):
if force:
print(f"Forcibly deleting user: {username}")
else:
print(f"Deleting user: {username}")

if __name__ == "__main__":
app()

Advanced Parameter Configuration

Use Annotated to define complex requirements like environment variables or numeric ranges.

from typing import Annotated
import typer

app = typer.Typer()

@app.command()
def process(
items: Annotated[int, typer.Argument(help="Number of items", min=1, max=100)],
config: Annotated[str, typer.Option(envvar="APP_CONFIG")] = "default.ini"
):
print(f"Processing {items} items with config {config}")

When to Use Typer

  • Use it when you want to build professional, user-friendly CLIs quickly.
  • Use it when you are already using type hints and want to avoid duplicating definitions.
  • Use it when you need subcommands, automatic shell completion, or integration with environment variables.

When Not to Use Typer

  • Avoid it if you are targeting environments with extremely strict dependency limits (Typer depends on Click and others).
  • Avoid it if you need to build a non-standard CLI that doesn't follow the "command/subcommand" pattern (though Click's flexibility usually covers this).

Integration & Stack Compatibility

  • Click: Typer is built on Click. You can mix Typer apps with Click objects.
  • Rich: Automatically used for beautiful terminal formatting if installed.
  • Shellingham: Used to detect the current shell for automatic completion installation.
  • Pydantic: Often used alongside Typer for complex data validation within the command logic.

Getting Started Pointers

Limitations

  • Type Hint Dependency: Typer relies heavily on type hints; if your codebase doesn't use them, you won't get the full benefits.
  • Startup Overhead: While minimal, the inspection of functions at runtime adds a small overhead compared to raw argparse.

FAQ

  • Is Typer faster than Click? It's built on Click, so the execution speed is nearly identical. The development speed is where Typer shines.
  • Can I use it with async functions? Typer itself is synchronous. To run async code, you typically call anyio.run() or asyncio.run() inside your command function.
  • How do I disable Rich formatting? Set the environment variable TYPER_USE_RICH=0.
  • Does it support shell completion? Yes, Typer can automatically install completion for Bash, Zsh, Fish, and PowerShell.