TypedDict en Python: diccionarios tipados

Publicado el: 28/08/2026
Tempo de leitura: 5 minutos
Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.

Los diccionarios de Python son flexibles, pero esa flexibilidad puede ocultar errores. Un payload puede exigir id, nombre y activo, mientras el código describe todo como dict[str, object]. Con ese tipo genérico, el analizador no sabe qué claves existen, cuáles son opcionales ni qué tipo corresponde a cada clave. typing.TypedDict permite describir la estructura esperada de un diccionario sin cambiar su comportamiento en runtime.

Esta guía cubre declaraciones básicas, claves obligatorias y opcionales, NotRequired, Required, herencia, payloads anidados, variantes discriminadas y la diferencia entre tipado estático y validación real.

Qué representa TypedDict

TypedDict es una declaración de tipo para diccionarios con claves conocidas. Durante la ejecución, el valor sigue siendo un dict normal.

from typing import TypedDict

class Usuario(TypedDict):
    id: int
    nombre: str
    activo: bool

usuario: Usuario = {
    "id": 10,
    "nombre": "Ana",
    "activo": True,
}

Mypy o Pyright puede detectar una clave ausente, un nombre mal escrito o un valor incompatible.

usuario_erroneo: Usuario = {
    "id": "10",       # se esperaba int
    "nombre": "Ana",
    "activo": True,
}

Python no impide esa asignación durante la ejecución. La protección aparece al ejecutar un analizador estático.

Por qué dict[str, object] no basta

dict[str, object] solo indica que las claves son strings y los valores pueden ser cualquier objeto. No relaciona cada clave con un tipo útil.

def mostrar(usuario: dict[str, object]) -> str:
    return usuario["nombre"].upper()  # object no garantiza upper()

Con TypedDict, el analizador sabe que usuario["nombre"] es str.

def mostrar(usuario: Usuario) -> str:
    return usuario["nombre"].upper()

Esta precisión amplía lo explicado en la guía de type hints en Python.

Claves opcionales con total=False

Por defecto, todas las claves son obligatorias. Usa total=False cuando todas puedan faltar.

class ActualizacionUsuario(TypedDict, total=False):
    nombre: str
    activo: bool
    email: str

Es útil para operaciones PATCH, donde el cliente envía solo los campos que desea modificar.

def actualizar_usuario(usuario_id: int, datos: ActualizacionUsuario) -> None:
    if "nombre" in datos:
        print(datos["nombre"])

Una clave opcional no significa que su valor acepte None. La clave puede faltar; si existe, debe contener el tipo declarado.

NotRequired y Required

Usa NotRequired cuando solo algunos campos puedan faltar. Usa Required dentro de un TypedDict con total=False cuando una clave deba seguir siendo obligatoria.

from typing import NotRequired, Required, TypedDict

class Perfil(TypedDict):
    id: int
    nombre: str
    apodo: NotRequired[str]
    foto: NotRequired[str]

class EventoParcial(TypedDict, total=False):
    tipo: Required[str]
    payload: object
    origen: str

Estos marcadores mantienen el contrato legible sin crear varios tipos casi idénticos.

Herencia y composición

Un TypedDict puede heredar campos de otro TypedDict.

class Entidad(TypedDict):
    id: int

class Producto(Entidad):
    nombre: str
    precio: float
    stock: int

La herencia funciona bien para grupos pequeños de campos comunes. Sin embargo, las jerarquías complejas dificultan la evolución. Separa los esquemas de creación, actualización, respuesta y persistencia cuando tengan requisitos distintos.

Sintaxis funcional

La forma funcional es útil cuando las claves no son identificadores Python válidos.

Cabeceras = TypedDict(
    "Cabeceras",
    {
        "content-type": str,
        "x-request-id": str,
    },
)

Para nombres normales, la sintaxis de clase suele ser más clara y fácil de documentar.

Modelar respuestas de APIs

TypedDict es apropiado para payloads internos o respuestas externas después de validarlas.

class Direccion(TypedDict):
    ciudad: str
    provincia: str
    codigo_postal: str

class ClienteAPI(TypedDict):
    id: int
    nombre: str
    direccion: Direccion
    etiquetas: list[str]

def ciudad_cliente(cliente: ClienteAPI) -> str:
    return cliente["direccion"]["ciudad"]

No anotes directamente response.json() como un TypedDict confiable. Las respuestas pueden omitir claves, cambiar formatos o contener valores inesperados.

TypedDict no valida en runtime

La declaración no convierte valores, no rechaza claves extra y no produce errores de validación. JSON, formularios, colas y registros de base de datos siguen necesitando validación real.

import json

texto = '{"id": "diez", "nombre": 99, "activo": true}'
datos = json.loads(texto)
# datos existe aunque sea incompatible con Usuario

Una frontera segura valida el objeto primero y solo después devuelve o refina el tipo. Pydantic, dataclasses con parsing explícito, attrs o validadores propios pueden cumplir esa función.

Refinar claves opcionales

Cuando un campo usa NotRequired, comprueba su presencia antes de indexarlo.

class Resultado(TypedDict):
    valor: int
    aviso: NotRequired[str]

def imprimir(resultado: Resultado) -> None:
    print(resultado["valor"])
    if "aviso" in resultado:
        print(resultado["aviso"])

get() también está disponible, pero su resultado normalmente incluye None y exige tratamiento.

Compatibilidad estructural y claves extra

La compatibilidad de TypedDict es estructural. Un tipo con más campos puede aceptarse donde se espera una forma menor, pero la obligatoriedad, la mutabilidad y los tipos de valor imponen restricciones.

Evita depender de reglas poco intuitivas. Mantén los contratos pequeños y ejecuta el mismo analizador en desarrollo y CI.

TypedDict como entrada y salida

class NuevoPedido(TypedDict):
    cliente_id: int
    items: list[int]
    cupon: NotRequired[str]

class PedidoCreado(TypedDict):
    id: int
    estado: str
    total: float

def crear_pedido(datos: NuevoPedido) -> PedidoCreado:
    return {
        "id": 501,
        "estado": "creado",
        "total": 149.90,
    }

La firma documenta la frontera de la función y mejora autocompletado, revisión y refactorización.

Variantes discriminadas con Literal

TypedDict combina especialmente bien con Literal.

from typing import Literal

class Exito(TypedDict):
    tipo: Literal["exito"]
    valor: int

class Fallo(TypedDict):
    tipo: Literal["fallo"]
    error: str

Respuesta = Exito | Fallo

def procesar(respuesta: Respuesta) -> str:
    if respuesta["tipo"] == "exito":
        return str(respuesta["valor"])
    return respuesta["error"]

El discriminador permite que el analizador reduzca la unión a la forma correcta.

Evolución de esquemas

Al evolucionar una API, añadir campos opcionales suele ser menos disruptivo que cambiar claves obligatorias. Eliminar un campo o modificar su tipo debería versionarse o migrarse con cuidado. TypedDict ayuda a localizar consumidores afectados.

El ecosistema moderno de typing también incorpora conceptos de campos de solo lectura. Verifica el soporte en la versión de Python y el analizador del proyecto antes de usar características recientes en una biblioteca pública.

Errores comunes

  • Usar TypedDict como validación JSON: solo aporta metadatos estáticos.
  • Confundir ausencia con None: opcionalidad de clave y valor nullable son distintos.
  • Usar un esquema para todas las operaciones: creación, patch y respuesta suelen necesitar tipos separados.
  • Aplicar cast a datos no validados: el cast silencia el analizador, pero no corrige valores.
  • Declarar todos los campos como object: se pierde casi toda la precisión.
  • Ignorar obligatoriedad y mutabilidad: la compatibilidad estructural también tiene reglas.

TypedDict, dataclass o Pydantic

Elige TypedDict cuando el valor de runtime debe seguir siendo un diccionario y la necesidad principal es el análisis estático. Elige dataclass cuando quieras objetos con atributos, métodos y construcción explícita. Elige Pydantic cuando debas validar, convertir o documentar datos externos.

Las herramientas pueden coexistir. Una capa de entrada puede validar con Pydantic y luego entregar estructuras TypedDict ligeras a código que integra bibliotecas basadas en diccionarios.

Conclusión

typing.TypedDict convierte diccionarios informales en contratos verificables. Describe claves obligatorias, opcionales, estructuras anidadas y variantes discriminadas sin alterar el objeto en runtime.

La documentación oficial de TypedDict en Python cubre totality, herencia y marcadores de obligatoriedad. Úsalo para formas conocidas, ejecuta un analizador estático y conserva validación real en todas las fronteras no confiables.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Protocol en Python: tipado estructural

    Aprende Python Protocol para tipado estructural, contratos genéricos, callbacks, runtime_checkable, pruebas e inyección de dependencias.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview en Python: buffers sin copia

    Aprende memoryview en Python para buffers sin copia, slices, bytearray editable, cast, mmap, struct, sockets y control seguro del ciclo

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc en Python: rastrea memoria

    Aprende tracemalloc en Python para medir picos, crear y comparar snapshots, filtrar asignaciones y diagnosticar crecimiento de memoria.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    queue en Python: comunica threads

    Aprende queue en Python para comunicar threads con FIFO, LIFO, prioridad, backpressure, task tracking, sentinelas y shutdown seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Flat lay of a complete toolset neatly organized in a workshop setting, essential for auto repair tasks.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    selectors en Python: varios sockets

    Aprende selectors en Python para multiplexar sockets, gestionar lecturas y escrituras parciales, buffers, timeouts, wakeup y backpressure.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Detailed close-up texture of a snake's patterned skin showcasing natural patterns and scales.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    contextvars en Python: contexto async

    Aprende contextvars en Python para contexto por task, request IDs, logging, copy_context, propagación a threads y restauración con tokens.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026