# How Pyrefly Handles TypedDict total and non-total Requirements

> Discover how Pyrefly manages TypedDict total and non-total requirements. Learn about its class-based approach, flag initialization, and metadata merging for field constraints and inheritance.

- Repository: [Meta/pyrefly](https://github.com/facebook/pyrefly)
- Tags: deep-dive
- Published: 2026-05-21

---

**Pyrefly treats TypedDict as a class-based construct where the `is_total` flag initializes to `true` and flips based on keyword arguments, with field requirements calculated through metadata merging that respects inheritance constraints and newer `closed`/`extra_items` keywords.**

The facebook/pyrefly repository implements a Rust-based Python type checker that enforces PEP 589 TypedDict specifications through precise metadata extraction and field-level calculations. Understanding how Pyrefly handles TypedDict total and non-total requirements reveals the mechanisms behind optional field detection, inheritance validation, and strict dictionary key constraints.

## Class Metadata Extraction: Parsing total and closed Keywords

Pyrefly extracts TypedDict configuration through the `typed_dict_metadata` method in [`pyrefly/lib/alt/class/class_metadata.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/class/class_metadata.rs) (lines 668‑692). This function walks class-level keyword arguments to determine field requirements and extra-item policies.

The implementation initializes `is_total = true` as the default state. When encountering `total: False`, the flag flips to `false`; conversely, `total: True` leaves the flag unchanged. This boolean directly determines whether fields become required or optional during the subsequent field calculation phase.

The method also handles newer TypedDict syntax extensions (lines 672‑697). The `closed` and `extra_items` keywords are parsed here, with `closed=True` mapping to `ExtraItems::Closed` and `closed=False` yielding `ExtraItems::Default`. These options are mutually exclusive—supplying both triggers a `BadTypedDict` error diagnostic.

## Field-Level Calculation: Required vs Optional Fields

After metadata extraction, `calculate_typed_dict_metadata_fields` merges fields from the current class with those inherited from base TypedDicts. This function respects the `is_total` flag established during the keyword parsing phase.

When `is_total` is `false`, every field receives **optional** status (`NotRequired`) unless a base class explicitly marks it as `Required`. Conversely, when `is_total` remains `true`, fields default to required unless explicitly annotated otherwise. The `TypedDictMetadata` struct stores these requirements in a `SmallMap<Name, bool>` where the boolean tracks the total status per field.

The solver later uses this map to enforce read-only (`ReadOnly`) and required (`Required`) markers when deriving concrete types from the TypedDict definition.

## Type Conversion Pipeline and Partial Views

When TypedDict definitions enter the type-solver pipeline, [`pyrefly/lib/tsp/type_conversion.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/tsp/type_conversion.rs) (lines 200‑218) converts them into class declarations through a dedicated match arm. Named TypedDicts generate backing class objects, while anonymous TypedDicts fall back to the built-in `TypedDict` type.

The same conversion logic handles `PartialTypedDict`, which represents incremental analysis views where certain fields may be temporarily absent. This ensures consistent behavior between named class definitions and inline TypedDict type hints during constraint solving.

## Inheritance Rules and Constraint Propagation

Pyrefly walks the base-class chain using `bases_with_metadata` to merge parent TypedDict constraints with child definitions. The inheritance logic preserves the most restrictive requirements: a child cannot convert a parent-required field into an optional one, even if the child specifies `total=False`.

This validation appears in the test suite at [`pyrefly/lib/test/typed_dict.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/test/typed_dict.rs) (lines 262‑272), which verifies that inheritance violations raise appropriate errors. The merging algorithm combines parent `total` flags and `extra_items` markers with child values, ensuring constraint monotonicity across the inheritance hierarchy.

## Error Handling and Validation

Invalid TypedDict configurations trigger the `BadTypedDict` diagnostic. Pyrefly emits errors for:

- Contradictory use of `closed` and `extra_items` keywords
- Non-literal values supplied for `total` or `closed` arguments
- Unsupported keywords in the TypedDict definition
- Inheritance violations where child classes attempt to relax required fields from parent classes

## Code Examples: total and closed in Practice

```python
from typing import TypedDict, Required, NotRequired

# 1. Default total=True (both fields required)

class Point(TypedDict):
    x: int
    y: int

# 2. Non-total (total=False makes fields optional)

class PartialPoint(TypedDict, total=False):
    x: int
    y: int

# 3. Closed TypedDict (rejects extra keys)

class StrictPoint(TypedDict, total=False, closed=True):
    x: int
    y: int

# 4. Illegal inheritance (parent requires, child cannot make optional)

class Base(TypedDict):
    a: int

class Child(Base, total=False):  # Error: cannot make 'a' optional

    pass

```

## Summary

- Pyrefly initializes `is_total = true` by default, flipping to `false` only when the class keyword argument `total=False` is explicitly detected in [`class_metadata.rs`](https://github.com/facebook/pyrefly/blob/main/class_metadata.rs).
- Field requirements are calculated through `calculate_typed_dict_metadata_fields`, which marks fields as `NotRequired` when `is_total` is `false` while preserving explicit `Required` annotations from base classes.
- The `closed` and `extra_items` keywords are parsed mutually exclusively; contradictory usage raises `BadTypedDict` errors during metadata extraction.
- Inheritance constraints propagate through `bases_with_metadata`, preventing child classes from relaxing required fields defined in parent TypedDicts.
- Type conversion occurs in [`type_conversion.rs`](https://github.com/facebook/pyrefly/blob/main/type_conversion.rs), handling both named TypedDicts and `PartialTypedDict` views for incremental analysis.

## Frequently Asked Questions

### What happens when a child TypedDict sets total=False but the parent has total=True?

Pyrefly enforces that child classes cannot relax requirements from parent TypedDicts. Even if the child specifies `total=False`, any fields declared required in the parent remain required in the child. Attempting to override this triggers a `BadTypedDict` inheritance error, as verified in [`pyrefly/lib/test/typed_dict.rs`](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/test/typed_dict.rs).

### How does Pyrefly handle the newer closed keyword in TypedDict?

The `closed` keyword is parsed in `typed_dict_metadata` (lines 672‑697), where `closed=True` creates an `ExtraItems::Closed` marker that rejects undeclared keys, while `closed=False` uses `ExtraItems::Default`. This keyword is mutually exclusive with `extra_items`; using both raises a `BadTypedDict` diagnostic.

### What is PartialTypedDict in Pyrefly's implementation?

`PartialTypedDict` represents an incremental analysis view where certain fields may be temporarily absent from the dictionary. During type conversion in [`type_conversion.rs`](https://github.com/facebook/pyrefly/blob/main/type_conversion.rs), these partial views follow the same conversion path as standard TypedDicts, ensuring consistent constraint solving during progressive type checking.

### How does Pyrefly report errors for invalid TypedDict configurations?

Pyrefly emits `BadTypedDict` diagnostics for invalid keyword combinations, non-literal values for `total` or `closed`, unsupported arguments, and inheritance constraint violations. These errors originate from the metadata extraction phase in [`class_metadata.rs`](https://github.com/facebook/pyrefly/blob/main/class_metadata.rs) and surface during class body analysis.