其他
在 Luau 中做类型标注、泛型与联合类型时…
适用于 Luau 类型系统相关工作:类型注解、泛型、联合类型、类型收窄、sealed/unsealed 表、严格度模式、模块类型导出与元表对象类型。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
在 Luau 中做类型标注、泛型与联合类特定
解决思路
- 严格度模式:维护中的代码用 `--!strict`,迁移过渡期用 `--!nonstrict`,只有遗留/生成的代码用 `--!nocheck`;模式由项目设置和指令共同决定,不要假设存在某个全局默认值。
- 推断哲学:先让编译器推断,只在边界(参数、返回值、导出)加注解;不要给每个 local 都加注解,噪音会掩盖信号。
- Sealed 与 unsealed 表:`local t = {}` 是 unsealed,可以继续加字段(`t.x = 1` 合法);`local t: {x: number} = {x=1}` 是 sealed,再加新字段会报错(`t.y = 2` 报 ERROR)。做法是先把表完整构建好,再加类型注解;把表传入函数或从函数返回都会使其 sealed。
- 联合类型与标签联合:用 `local id: string | number = "abc"` 这类联合;用带判别字段的联合建模状态,如 `type State<T> = {kind:"loading"} | {kind:"ready", value:T} | {kind:"fail", msg:string}`;通过判别字段收窄,例如 `if state.kind == "ready" then` 之后 `state.value` 即被收窄。
- 类型收窄:用 `typeof(value) == "string"` 收窄基础类型;用 `instance:IsA("BasePart")` 收窄 Instance;用 `assert(optionalValue, "missing")` 做非 nil 收窄。
- 泛型:当输入类型到输出类型的映射重要时使用泛型,如 `function first<T>(list: {T}): T?`;也可用泛型别名,如 `type Result<T> = {success: boolean, value: T?}`;绝不用 `any` 替代泛型。
- 类型导出:在模块边界用 `export type Foo = {...}` 导出类型,使用方通过 `require` 后访问 `Types.Foo`。
- 对象类型:用 `export type Counter = typeof(setmetatable({} :: CounterData, Counter))` 获得精确的 self 类型。
- 类型转换(`::`):只作为精确化工具,用来收窄过于宽泛的推断结果,绝不用来隐藏错误。
- 写可信的类型:注解是给编译器的契约,不是运行时有效性的证明;对信任边界(remotes、DataStores、HttpService、attributes)即使全部标注了类型,仍要做运行时检查;在信任边界内部则让类型承担职责,不必每次调用都重复检查。
- 常见错误要避免:nonstrict 下未 sealed 导致的 `any` 传播、过早 seal 表、没有判别字段的联合、给每个 local 都加注解。
适用边界 · 注意事项
- 纯语法问题不要用本技能,改用 `roblox-luau-core`;OOP/异步/模块模式问题改用 `roblox-luau-patterns`。
- 不要用 `::` 类型转换去消除报错而不是精确化推断,也不要把类型注解当作对不可信输入的运行时校验替代品。