typing.assert_type y typing.reveal_type ayudan a comprender y probar la inferencia estática. reveal_type() pide al analizador que muestre el tipo inferido de una expresión. assert_type() declara el tipo esperado y convierte esa expectativa en una prueba estática que falla cuando cambia la inferencia.
Son herramientas útiles para bibliotecas, overloads, genéricos, Protocol, TypedDict, decoradores y funciones de refinamiento. Esta guía cubre exploración, pruebas automatizadas, CI, comportamiento de runtime, Any, tipos variádicos, Self y diferencias entre analizadores.
Qué hace reveal_type
from typing import reveal_type
valor = [1, 2, 3]
reveal_type(valor)Un analizador puede informar list[int]. El diagnóstico aparece durante el análisis estático. El comportamiento de runtime es secundario y puede imprimir un mensaje.
Revelar expresiones intermedias
datos: dict[str, int | None] = {"a": 1}
valor = datos.get("a")
reveal_type(valor)El resultado debería ser int | None. Eso explica por qué se necesita una comprobación antes de realizar aritmética.
Refinamiento por flujo
if valor is not None:
reveal_type(valor)
print(valor + 1)Dentro de la rama, el tipo debería reducirse a int.
Qué hace assert_type
from typing import assert_type
resultado = len("python")
assert_type(resultado, int)El analizador compara el tipo inferido con el esperado. En runtime, la función devuelve el primer argumento y no lo convierte ni valida.
assert_type no es isinstance
valor: object = 10
assert_type(valor, int) # debería fallar estáticamenteEl objeto concreto es entero, pero el tipo estático declarado es object. assert_type prueba la información estática, no el valor observado.
Probar una función genérica
from typing import TypeVar
T = TypeVar("T")
def primero(items: list[T]) -> T:
return items[0]
assert_type(primero([1, 2]), int)
assert_type(primero(["a", "b"]), str)Las pruebas confirman que el parámetro genérico se propaga al retorno.
Probar overloads
from typing import Literal, overload
@overload
def leer(*, binario: Literal[False] = False) -> str: ...
@overload
def leer(*, binario: Literal[True]) -> bytes: ...
def leer(*, binario: bool = False) -> str | bytes:
return b"datos" if binario else "datos"
assert_type(leer(), str)
assert_type(leer(binario=True), bytes)Cambiar el orden o las firmas puede degradar la inferencia. assert_type detecta la regresión sin ejecutar la función.
Probar Literal
modo = "rapido"
reveal_type(modo)
modo_literal: Literal["rapido"] = "rapido"
assert_type(modo_literal, Literal["rapido"])Una variable mutable puede ampliarse a str, mientras que la anotación conserva el valor literal.
Probar TypedDict
from typing import TypedDict
class Usuario(TypedDict):
id: int
nombre: str
usuario: Usuario = {"id": 1, "nombre": "Ana"}
assert_type(usuario["id"], int)
assert_type(usuario["nombre"], str)Las claves opcionales deben comprobarse antes del acceso. reveal_type ayuda antes y después de in.
Probar TypeGuard y TypeIs
from typing import TypeGuard
def es_str(valor: object) -> TypeGuard[str]:
return isinstance(valor, str)
item: object = "x"
if es_str(item):
assert_type(item, str)La prueba confirma el refinamiento verdadero. Para TypeIs, prueba también la rama falsa. Consulta TypeGuard en Python.
Probar Protocol
from typing import Protocol
class Cerrable(Protocol):
def cerrar(self) -> None: ...
class Archivo:
def cerrar(self) -> None: ...
archivo = Archivo()
assert_type(archivo, Archivo)
cerrable: Cerrable = archivo
assert_type(cerrable, Cerrable)assert_type comprueba el tipo estático de la variable, no solo compatibilidad estructural.
Decoradores y pérdida de firma
@mi_decorador
def buscar(id_: int) -> str:
...
reveal_type(buscar)
assert_type(buscar(1), str)Decoradores mal tipados convierten funciones precisas en Callable[..., Any]. ParamSpec puede preservar el contrato. Consulta ParamSpec en Python.
Archivos de pruebas estáticas
Crea archivos como tests/typing/test_api.py. No necesitan ejecutarse con pytest; el CI ejecuta el analizador sobre ellos. Documentan la experiencia esperada del consumidor.
Pruebas positivas y negativas
assert_type expresa inferencia correcta. Las llamadas que deben fallar requieren comentarios o herramientas específicas del analizador. Mantén los casos negativos cerca de la API protegida.
No dependas del texto del diagnóstico
Los mensajes de reveal_type cambian entre analizadores. Usa assert_type para expectativas automatizadas y reveal_type para exploración.
Tipos equivalentes
Sintaxis distintas pueden representar el mismo tipo y el orden de uniones puede variar. El analizador decide equivalencia sin comparar strings.
Any puede ocultar problemas
Si una dependencia devuelve Any, muchas operaciones pasan sin verificar. Añade pruebas en las fronteras y configura reglas estrictas. reveal_type muestra rápidamente dónde entró Any.
Never y ramas inalcanzables
En código exhaustivo, reveal_type puede mostrar Never. La guía de Never en Python explica assert_never().
Self y métodos fluidos
builder = BuilderEspecial().configurar()
assert_type(builder, BuilderEspecial)La prueba garantiza que un método con Self conserva subclases.
Tipos variádicos
resultado = agregar_prefijo((1, "a"))
assert_type(resultado, tuple[str, int, str])TypeVarTuple y Unpack pueden producir inferencias complejas. Las aserciones documentan la relación posicional.
Comportamiento de runtime
assert_type(valor, Tipo) devuelve valor sin comprobarlo. reveal_type tampoco sustituye validación. No uses estas funciones para proteger entradas de usuario.
Eliminar reveals exploratorios
reveal_type es útil durante desarrollo, pero puede generar salida o ensuciar el código. Mantenlo en pruebas dedicadas o elimínalo después del diagnóstico.
Compatibilidad
Usa typing_extensions.assert_type cuando sea necesario. El comportamiento estático depende mucho de la versión del analizador. Fija versiones en CI y revisa actualizaciones.
Comparar analizadores
Mypy y pyright pueden inferir casos complejos de forma diferente. Si la biblioteca soporta ambos, ejecuta la misma suite con los dos y evita depender de comportamientos no especificados.
Errores comunes
- Esperar validación de runtime: assert_type no llama isinstance.
- Dejar reveal_type en producción: puede producir salida innecesaria.
- Probar solo internos: prueba la API del consumidor.
- Permitir que Any se propague: las aserciones pierden valor.
- Comparar strings de diagnóstico: el texto cambia.
- No fijar el analizador: las actualizaciones alteran inferencia.
Ejemplo completo: API de caché
from typing import TypeVar, overload, assert_type
T = TypeVar("T")
_AUSENTE = object()
@overload
def obtener(clave: str) -> object: ...
@overload
def obtener(clave: str, default: T) -> object | T: ...
def obtener(clave: str, default: object = _AUSENTE) -> object:
...
assert_type(obtener("x"), object)
assert_type(obtener("x", 0), object | int)
assert_type(obtener("x", None), object | None)Las pruebas documentan la relación entre default y retorno. El CI informa cuando overloads o stubs cambian.
Estrategia para bibliotecas
Crea archivos de consumidores reales. Importa el paquete instalado. Prueba retornos, errores esperados, subclases, overloads, genéricos y stubs. El tipado público puede romperse aunque los tests de runtime sigan verdes.
Conclusión
reveal_type() permite investigar la inferencia. assert_type() convierte una expectativa en una prueba de regresión. Juntos hacen las APIs tipadas más predecibles.
La documentación oficial de assert_type y reveal_type en Python define su comportamiento. Usa reveal_type para explorar, assert_type para automatizar y ejecuta las pruebas con las configuraciones exactas soportadas por el proyecto.







