Luau 中 Signal.new 的返回类型为何不匹配?
数据阿明
结论
报错是因为 Signal 被声明为普通结构化表类型,而 setmetatable 返回的是带有 Signal 元表的表。两者的类型形状不一致,导致返回类型检查失败。:: Signal 也不能将这两个不相关的类型强行转换。
要让 Signal 实例类型包含 Signal 元表,可以先定义实例字段,再通过 typeof(setmetatable(...)) 声明导出的类型:
--!strict
local Signal = {}
Signal.__index = Signal
type SignalData = {
_callbacks: { (any) -> () },
_waiting: { thread },
}
export type Connection = {
Disconnect: () -> (),
}
export type Signal = typeof(setmetatable({} :: SignalData, Signal))
function Signal.new(): Signal
return setmetatable({
_callbacks = {},
_waiting = {},
}, Signal)
end
这样,Signal 表示带有 Signal 元表的实例,Signal.new() 的返回类型也与函数标注一致。原代码中的 Connect、Disconnect、Fire 和 Wait 等方法仍可定义在 Signal 上,实例会通过 __index 使用这些方法。
为什么 :: Signal 没有解决问题
原先的 export type Signal = { ... } 描述普通表,并在字段中列出了方法。实际表达式的类型则带有 @metatable Signal。类型检查器无法对应这两种类型,因此类型断言也无法消除它们之间的不兼容,最终报告 types are unrelated。
在类型定义中说明实例使用的 metatable 后,就可以移除返回值上的 :: Signal,让 setmetatable 的结果直接通过返回类型检查。
检查要点
SignalData描述实例自身存储的字段,例如_callbacks和_waiting。Signal.__index = Signal指定实例查找方法时使用Signal表。typeof(setmetatable({} :: SignalData, Signal))将字段类型与 metatable 类型结合起来。- 后续修改字段或方法签名时,也要检查相关类型定义,确保实例结构与方法实现一致。
备注:内容仅供参考。
