使用TypeAlias别名Callable时mypy报错,Pyright正常,是否为mypy Bug?
问题:TypeAlias封装ParamSpec相关Callable导致mypy报错,Pyright正常
场景还原
原本的代码直接使用Callable[Concatenate[BaseContext, Params], None]标注装饰器参数时,Pyright和mypy均能通过静态类型检查。但将该Callable类型封装为TypeAlias后,Pyright仍正常,mypy出现类型不兼容错误。
原始可正常通过检查的代码
from typing import Callable, Concatenate, ParamSpec, TypeAlias class BaseContext: ... Params = ParamSpec("Params") def reset_command_context( _func: Callable[Concatenate[BaseContext, Params], None], *, current: bool = False, ) -> None: ... @reset_command_context def process_invite_unique(ctx: BaseContext, unique: bool) -> None: ... @reset_command_context def process_ban(ctx: BaseContext) -> None: ...
修改后触发mypy错误的代码
仅修改装饰器参数的类型标注方式,新增TypeAlias并替换原类型:
FuncSpec: TypeAlias = Callable[Concatenate[BaseContext, Params], None] def reset_command_context( _func: FuncSpec, *, current: bool = False, ) -> None: ...
mypy错误输出
~ $ mypy test.py test2.py:20: error: Argument 1 to "reset_command_context" has incompatible type "Callable[[BaseContext, bool], None]"; expected "Callable[[BaseContext, VarArg(Any), KwArg(Any)], None]" [arg-type] test2.py:20: note: This is likely because "process_invite_unique" has named arguments: "ctx". Consider marking them positional-only test2.py:25: error: Argument 1 to "reset_command_context" has incompatible type "Callable[[BaseContext], None]"; expected "Callable[[BaseContext, VarArg(Any), KwArg(Any)], None]" [arg-type] test2.py:25: note: This is likely because "process_ban" has named arguments: "ctx". Consider marking them positional-only Found 2 errors in 1 file (checked 1 source file)
结论
这不是你的用法错误,而是mypy在处理带ParamSpec的TypeAlias时的实现限制(与Pyright的支持差异)。
当直接在函数参数中使用Callable[Concatenate[BaseContext, Params], None]时,mypy能够正确将Params绑定到被装饰函数的实际参数列表;但将该类型提取为TypeAlias后,mypy无法正确传递ParamSpec的绑定关系,错误地将Params解析为任意可变参数(VarArg和KwArg),导致与被装饰函数的固定参数列表不兼容。
可行的解决方式
- 暂时避免将带
ParamSpec的Callable封装为TypeAlias,直接在参数标注中使用完整类型表达式 - 升级到最新版本的mypy,部分后续版本可能已修复该类兼容性问题
- 若必须使用
TypeAlias,可尝试结合Generic显式携带ParamSpec:
from typing import Generic # 重新定义带泛型的TypeAlias FuncSpec = TypeVar('FuncSpec', bound=Callable[Concatenate[BaseContext, ParamSpec], None]) # 或者更严谨的泛型包装 class FuncSpec(Generic[Params]): __type__: TypeAlias = Callable[Concatenate[BaseContext, Params], None]
注:该写法需要mypy的较高版本支持,具体兼容性需自行验证。
内容的提问来源于stack exchange,提问作者dbzix
相关产品推荐
相关产品推荐

