TypedDict describe las claves y los tipos de valores que debe contener un diccionario para los verificadores estáticos. Durante la ejecución sigue siendo un dict común. Es útil para estructuras internas parecidas a JSON sin prometer validación automática.
Marcar claves obligatorias y opcionales
from typing import NotRequired, TypedDict
class UsuarioPayload(TypedDict):
id: int
nombre: str
alias: NotRequired[str]
def etiqueta(usuario: UsuarioPayload) -> str:
return usuario.get("alias", usuario["nombre"])
datos: UsuarioPayload = {"id": 7, "nombre": "Ana"}
print(etiqueta(datos))
La anotación permite detectar un id ausente, un valor incompatible o el acceso inseguro a una clave opcional. No crea un constructor: UsuarioPayload(id=7, nombre="Ana") solo usa la construcción común de dict con argumentos nombrados. isinstance(datos, UsuarioPayload) no está permitido.
Expresar obligatoriedad con precisión
La clase es total por defecto y todas las claves declaradas son obligatorias. total=False permite que todas falten, mientras Required y NotRequired modifican la decisión para cada clave.
from typing import NotRequired, Required, TypedDict
class ActualizacionUsuario(TypedDict, total=False):
id: Required[int]
nombre: str
email: str
motivo: NotRequired[str]
Aquí id debe existir y los demás campos pueden omitirse. La ausencia difiere de una clave presente con None: utiliza str | None solo si nulo es un valor aceptado. Una clave que puede faltar y aceptar nulo necesita ambas ideas, como email: NotRequired[str | None].
Los marcadores por clave aclaran contratos mixtos. total=False sigue siendo práctico para actualizaciones donde casi todo es opcional. Revisa la versión mínima de Python; proyectos antiguos pueden obtener estos marcadores mediante typing_extensions.
Validar antes de estrechar el tipo
Un decodificador JSON devuelve valores amplios. No ocultes el problema con cast(UsuarioPayload, bruto) antes de validar: cast() devuelve exactamente el mismo objeto y no comprueba nada.
from typing import Any, TypeGuard
def es_usuario(valor: Any) -> TypeGuard[UsuarioPayload]:
if not isinstance(valor, dict):
return False
if not isinstance(valor.get("id"), int):
return False
if not isinstance(valor.get("nombre"), str):
return False
alias = valor.get("alias")
return alias is None or isinstance(alias, str)
def leer_usuario(bruto: object) -> UsuarioPayload:
if not es_usuario(bruto):
raise ValueError("usuario invalido")
return bruto
Una validación real también comprueba nombres vacíos, valores permitidos, longitudes máximas y la política sobre claves desconocidas. Cuidado con bool: como es subclase de int, isinstance(True, int) es verdadero. Usa type(valor) is int cuando deban rechazarse booleanos.
TypeGuard informa al verificador que la rama exitosa tiene el tipo estrecho. No demuestra que la implementación sea correcta, así que prueba claves ausentes, tipos erróneos, nulos, booleanos y datos extra. Una biblioteca de esquemas puede convenir para estructuras grandes, pero TypedDict no es esa validación.
Reutilizar formas y consumir con seguridad
La herencia agrega claves sin cambiar el objeto durante la ejecución:
class Entidad(TypedDict):
id: int
class Usuario(Entidad):
nombre: str
alias: NotRequired[str]
Mantén jerarquías superficiales. Describen conjuntos de claves, no comportamiento orientado a objetos. Para fragmentos reutilizables, anidar un TypedDict dentro de otro puede expresar mejor el JSON.
Los diccionarios tipados poseen compatibilidad estructural: un valor a menudo puede incluir claves adicionales y satisfacer una función que exige una forma menor. Las reglas consideran obligatoriedad y mutabilidad, por lo que conviene seguir el resultado del verificador y no asumir reglas de clases normales.
Indexar una clave obligatoria es apropiado después de validar. Para NotRequired, comprueba la presencia o usa .get() resolviendo el fallback. No uses .get() sobre una clave obligatoria solo para callar un aviso, pues debilita la invariante.
Los verificadores suelen esperar claves literales. Un bucle sobre strings arbitrarios pierde la relación entre cada clave y su tipo. Si domina la iteración dinámica, Mapping[str, object], un diccionario uniforme o un modelo de validación puede ser mejor contrato.
Elegir la alternativa adecuada
Durante la revisión, separa claves obligatorias, opcionales y anulables. Prueba un objeto completo, el mínimo válido y la ausencia de cada clave obligatoria. Incluye tipos incorrectos, booleanos en lugar de enteros, nulos y claves extra. Ejecuta el verificador con un error deliberado para confirmar que la anotación participa en el alcance configurado.
Mantén los detalles de transporte en el borde. Si la lógica interna normaliza strings o resuelve ausencias repetidamente, transforma el diccionario validado en un modelo de dominio. No crees una clase solo para renombrar un JSON estable. La decisión útil concentra incertidumbre en la entrada y entrega un contrato sencillo a los llamadores.
Usa TypedDict para datos que ya tienen forma de diccionario en APIs, configuración o serialización. Usa dataclass para un valor controlado por la aplicación con métodos, valores predeterminados, campos derivados o igualdad relevante. Usa Protocol para describir comportamiento y atributos en vez de claves. Usa un parser explícito o un esquema en runtime cuando una entrada no confiable deba aceptarse o rechazarse.
Una frontera saludable decodifica JSON, valida, devuelve una estructura anotada con precisión y deja que el análisis estático ayude al código interno. Así se separan dos trabajos: evidencia durante la ejecución en la entrada y garantías de desarrollo en código confiable.
NotRequired indica una clave que puede faltar. Required hace lo contrario en una definición con total=False. Los marcadores por campo suelen expresar el contrato mejor que volver opcional todo el diccionario.
No trates TypedDict como prueba de que una respuesta HTTP es confiable. Verifica primero que el valor sea un objeto, que existan las claves y que los tipos y límites coincidan. Después entrega la estructura validada al resto de la aplicación.
Si el objeto necesita invariantes, métodos o identidad propia, considera una dataclass. La guía de type hints en Python explica cómo integrar estas anotaciones con análisis estático.
La documentación oficial de TypedDict, consultada el 22 de julio de 2026, detalla herencia, formas genéricas y claves obligatorias u opcionales.