# Typer — Build Python CLIs from Type Hints > Typer turns Python functions into CLI commands using only type hints. It is built on Click but removes the boilerplate — write a function, and Typer auto-generates parsing, help text, and shell completions. ## Install Save in your project root: # Typer — Build Python CLIs from Type Hints ## Quick Use ```bash pip install typer ``` ```python # main.py import typer app = typer.Typer() @app.command() def hello(name: str, formal: bool = False): msg = f"Good morning, {name}." if formal else f"Hello {name}!" typer.echo(msg) if __name__ == "__main__": app() ``` ```bash python main.py hello Alice python main.py hello Alice --formal python main.py --help ``` ## Introduction Typer is the "FastAPI of CLIs" — same author (Sebastián Ramírez), same type-hint-first philosophy. It wraps Click but lets you declare options and arguments just by writing a Python function signature. Your type hints become validation, and your docstrings become help text. With over 15,000 GitHub stars, Typer is used by dbt Cloud CLI, typer itself, and thousands of tools that want modern Python CLIs without Click boilerplate. ## What Typer Does Typer reads your function signature: parameters without defaults become required arguments, parameters with defaults become options, bool params become flags, and types (int, float, Path, Enum) become validators. It generates --help, shell completion, and nested subcommands automatically. ## Architecture Overview ``` @app.command() def fn(a: int, b: str = "x", flag: bool = False): ... | [Typer Introspection] Read signature + type hints | [Click Command Tree] (under the hood) | [Argv Parser] Validation + type coercion | [Your function called] | [Rich output] (auto-wired for pretty errors) ``` ## Self-Hosting & Configuration ```python import typer from typing import Optional from pathlib import Path from enum import Enum app = typer.Typer(help="Awesome CLI") users_app = typer.Typer(help="Manage users") app.add_typer(users_app, name="users") class Format(str, Enum): json = "json" yaml = "yaml" @app.command() def export( out: Path = typer.Argument(..., help="Output file"), fmt: Format = Format.json, verbose: bool = typer.Option(False, "--verbose", "-v"), ): """Export data to OUT in the chosen format.""" if verbose: typer.secho(f"Exporting to {out} as {fmt.value}", fg="green") out.write_text("...") @users_app.command("add") def users_add(name: str, admin: bool = False): typer.echo(f"Added {name} (admin={admin})") if __name__ == "__main__": app() ``` ## Key Features - **Type hints become parsers** — int, float, Path, bool, Enum, Optional - **Subcommands** — nest Typer apps for git-style command trees - **Auto-generated help** — from docstrings and parameter metadata - **Shell completions** — `--install-completion` for bash/zsh/fish/PowerShell - **Prompting** — `typer.prompt()` and `typer.confirm()` for interactive input - **Rich integration** — colorful tracebacks and prompts - **Testable** — `CliRunner` from Click works unchanged - **FastAPI DX** — same ergonomics for developers who love FastAPI ## Comparison with Similar Tools | Feature | Typer | Click | argparse | Fire | docopt | |---|---|---|---|---|---| | Type hints drive parsing | Yes | No (decorators) | No | Partial | No (docstring) | | Subcommands | Yes | Yes | Nested | Yes | Limited | | Auto completion | Yes | Yes | No | No | No | | Learning Curve | Very Low | Low | Moderate | Very Low | Moderate | | Built on | Click | Standalone | stdlib | Inspect | Docstring DSL | | Best For | Modern Python | Any Python CLI | Stdlib only | Quick scripts | Declarative | ## FAQ **Q: Typer vs Click — do I need both?** A: Typer is a layer on top of Click. Use Typer for new projects; drop down to Click features when you need advanced custom types or parsers. **Q: How do I test a Typer app?** A: `from typer.testing import CliRunner`. Invoke with args and check `result.exit_code` and `result.stdout`. **Q: Does Typer support async commands?** A: Not directly — Typer invokes sync functions. Use `asyncio.run(...)` inside your command, or wrap with a small helper. **Q: Can I use Typer without global app state?** A: Yes. You can use `@typer.run` on a single function for one-off scripts, no `Typer()` instance needed. ## Sources - GitHub: https://github.com/fastapi/typer - Docs: https://typer.tiangolo.com - Author: Sebastián Ramírez (tiangolo) - License: MIT --- Source: https://tokrepo.com/en/workflows/6942f03e-37b5-11f1-9bc6-00163e2b0d79 Author: AI Open Source