# What is PEP 695?


[PEP 695: Type Parameter Syntax](https://peps.python.org/pep-0695/) (Python 3.12) builds generics into the language. `class Box[T]:` declares a generic class, `def first[T](xs: list[T]) -> T:` a generic function, and `type Maybe[T] = T | None` a type alias, with no `TypeVar` imports and no `Generic` base class.

Generics had been spelled through the `typing` module since [PEP 484](https://pydevtools.com/handbook/explanation/what-is-pep-484.md) introduced type hints. PEP 695 gives them syntax.

## What does the new syntax replace?

The before and after for a generic class:

```python
# Before 3.12
from typing import Generic, TypeVar

T = TypeVar("T")

class Box(Generic[T]):
    def __init__(self, item: T) -> None:
        self.item = item

# 3.12+
class Box[T]:
    def __init__(self, item: T) -> None:
        self.item = item
```

The new form fixes more than verbosity. The old `T = TypeVar("T")` was a module-level variable with no defined scope, shared across every function that referenced it; PEP 695 type parameters are scoped to the class or function that declares them. Variance is now inferred from how a parameter is used instead of declared with `covariant=True` flags. The declared parameters are introspectable at runtime via `Box.__type_params__`.

Constraints use a bound after a colon: `def largest[T: (int, float)](xs: list[T]) -> T:` or `class Sorted[T: Comparable]:`.

## How does the type statement work?

The `type` statement replaces `Alias = Union[...]` assignments and the `TypeAlias` annotation:

```python
type Maybe[T] = T | None
```

This creates a `typing.TypeAliasType` object whose value is evaluated lazily: the right-hand side isn't computed until something asks for `Maybe.__value__`. Forward references in an alias therefore work without quotes, the same idea [PEP 649](https://pydevtools.com/handbook/explanation/what-is-pep-649.md) later applied to all annotations.

## Can you use it yet?

The deciding question is your minimum supported Python, because PEP 695 is grammar. `typing_extensions` can backport objects like `ParamSpec`, but it cannot make the 3.9 parser accept `class Box[T]:`. Code that must run on 3.11 or older keeps the `TypeVar` spelling.

For code that's 3.12-only, support is solid across the toolchain:

- [mypy](https://pydevtools.com/handbook/reference/mypy.md), [Pyright](https://pydevtools.com/handbook/reference/pyright.md), and [ty](https://pydevtools.com/handbook/reference/ty.md) all check the new syntax, including inferring `int` from `first([1, 2, 3])` and rejecting `Box[int]("not an int")`. Pyright supported it at the 3.12 release; mypy's support arrived across several releases ([mypy issue #15238](https://github.com/python/mypy/issues/15238) tracked the rollout).
- [Ruff](https://pydevtools.com/handbook/reference/ruff.md) migrates old code automatically: `UP040` rewrites `TypeAlias` assignments to `type` statements, and `UP046`/`UP047` rewrite `Generic[T]` classes and `TypeVar` functions to the bracket syntax.

Python 3.13 extended the syntax with type parameter defaults ([PEP 696](https://peps.python.org/pep-0696/), default values like `class Box[T = str]:`).

## Learn More

- [PEP 695: Type Parameter Syntax](https://peps.python.org/pep-0695/)
- [Type parameter lists in the language reference](https://docs.python.org/3/reference/compound_stmts.html#type-params)
- [Ruff rule UP040: non-pep695-type-alias](https://docs.astral.sh/ruff/rules/non-pep695-type-alias/)
- [What is PEP 604?](https://pydevtools.com/handbook/explanation/what-is-pep-604.md) covers the matching modernization for unions
- [What is PEP 612?](https://pydevtools.com/handbook/explanation/what-is-pep-612.md) covers `ParamSpec`, which this syntax declares inline as `[**P]`
