luau-types

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

luau-types

luau-types

When to Use

适用场景

Use this skill when the task is primarily about Luau's type system:
  • Choosing between
    --!strict
    ,
    --!nonstrict
    , and other file type-checking modes.
  • Adding or correcting type annotations on variables, functions, tables, and modules.
  • Designing APIs that preserve inference instead of collapsing to
    any
    .
  • Modeling data with generics, unions, intersections, optionals, and tagged unions.
  • Typing object-like tables, metatable-backed modules, and exported module surfaces.
  • Writing or reviewing type functions and other advanced type-level utilities.
  • Using Roblox class, datatype, enum, or
    IsA
    knowledge only to improve static typing.
Do not use this skill when the task is mainly about:
  • General Luau syntax, control flow, metatables, or standard-library usage outside typing concerns.
  • Runtime performance, profiling, hot-path tuning, or allocation strategy.
  • Roblox networking, replication, data storage, cloud APIs, or gameplay architecture.
当任务主要涉及Luau类型系统时使用本指南:
  • --!strict
    --!nonstrict
    和其他文件类型检查模式之间做选择
  • 为变量、函数、表和模块添加或修正类型注解
  • 设计能够保留类型推导能力、不会退化为
    any
    类型的API
  • 使用泛型、联合类型、交叉类型、可选类型、标签联合类型建模数据
  • 为类对象表、基于元表的模块、导出的模块对外接口添加类型定义
  • 编写或评审类型函数及其他高级类型层面的工具方法
  • 仅在优化静态类型时使用Roblox类、数据类型、枚举或
    IsA
    相关知识

Decision Rules

不适用场景

  • Use this skill if the core question is "what should this type be?" or "how should this code type-check over time?"
  • Prefer
    --!strict
    guidance for new or actively maintained code unless the task explicitly targets transitional or legacy code.
  • Prefer inference-preserving designs over annotation-heavy designs when the inferred shape stays precise and readable.
  • Prefer explicit exported aliases at module boundaries when consumers should share a stable contract.
  • Use generics when the relationship between inputs and outputs matters; do not replace that relationship with
    any
    .
  • Use tagged unions plus refinements when a value can be one of several structured cases.
  • If the task shifts into pure language syntax, hand off to
    luau-core
    .
  • If the task shifts into optimization or runtime cost tradeoffs, hand off to
    luau-performance
    .
  • If the task requires Roblox runtime architecture beyond type names and type refinement, use the appropriate
    roblox/*
    skill instead.
  • If unsure, exclude anything that is not directly needed to improve typing correctness, maintainability, or analyzer behavior.
当任务主要涉及以下内容时不要使用本指南:
  • 通用Luau语法、控制流、元表,或和类型无关的标准库使用
  • 运行时性能、 profiling、热路径优化或内存分配策略
  • Roblox网络、数据同步、存储、云API或游戏玩法架构

Instructions

决策规则

  1. Start by identifying the file mode expectation:
    • --!strict
      for strong inference and early error detection.
    • --!nonstrict
      for transitional code where unresolved values would otherwise become noisy.
    • --!nocheck
      only when the task explicitly requires disabled analysis.
  2. Preserve useful inference before adding annotations everywhere. Add annotations where they clarify intent, stabilize module contracts, constrain
    self
    , or prevent unwanted widening to
    any
    .
  3. Prefer concrete aliases for shared shapes:
    • records for structured data,
    • indexers for dictionaries,
    • {T}
      for arrays,
    • exported aliases for module-facing contracts.
  4. Use optionals, unions, and intersections deliberately:
    • T?
      for
      T | nil
      ,
    • tagged unions for state machines or result-like values,
    • intersections to combine compatible table capabilities or function signatures.
  5. Treat casts with
    ::
    as a precision tool, not a bypass. Use them to narrow overly generic inference, not to hide unrelated-type errors.
  6. Design generics around relationships:
    • preserve element type through transforms,
    • carry key/value relationships through containers,
    • avoid defaulting to
      any
      when a type parameter can express intent.
  7. Model tables according to how Luau analyzes them:
    • unsealed tables can accumulate fields locally,
    • annotated or returned tables become sealed,
    • width subtyping applies to sealed records.
  8. For object-like modules, separate instance data from class behavior, derive the instance type from
    setmetatable
    , and annotate
    self
    explicitly when methods need the shared class type.
  9. Keep module API surfaces type-safe:
    • export named aliases for consumer-facing data,
    • keep implementation details internal,
    • choose signatures that infer caller types cleanly.
  10. Use Roblox type knowledge only for annotations and refinements, such as
    Instance
    ,
    Part
    ,
    Enum.Material
    , datatypes, and
    IsA
    -driven narrowing.
  • 如果核心问题是「这个类型应该是什么?」或「这段代码长期的类型检查应该如何运作?」,则使用本指南
  • 对于新代码或正在维护的代码,优先推荐
    --!strict
    模式,除非任务明确针对过渡阶段或遗留代码
  • 当推导出来的类型足够精确且易读时,优先选择保留类型推导的设计,而非大量使用注解的设计
  • 当模块消费者需要稳定的类型契约时,优先在模块边界处使用显式的导出别名
  • 当输入和输出之间的类型关联很重要时使用泛型,不要用
    any
    替代这种关联关系
  • 当一个值可能属于多种结构化类型时,使用标签联合类型加类型细化的方案
  • 如果任务转向纯语言语法相关问题,转交给
    luau-core
    指南处理
  • 如果任务转向优化或运行时成本权衡相关问题,转交给
    luau-performance
    指南处理
  • 如果任务需要用到除了类型名称和类型细化之外的Roblox运行时架构知识,使用对应的
    roblox/*
    指南
  • 如果不确定如何处理,排除所有和提升类型正确性、可维护性、分析器行为没有直接关系的内容

Using References

使用指引

  • Open
    references/type-system-overview.md
    for file modes, structural typing, annotations, casts, and module-boundary guidance.
  • Open
    references/basic-types-and-table-typing.md
    for builtin types, special types like
    any
    and
    unknown
    , function signatures, table states, and indexers.
  • Open
    references/generics.md
    for generic aliases, generic functions, defaults on aliases, and inference-preserving container patterns.
  • Open
    references/unions-and-intersections.md
    for result shapes, tagged unions, discriminants, and safe intersection usage.
  • Open
    references/refinements.md
    for truthy checks,
    type(...)
    guards, equality narrowing, compound conditions, and
    assert
    -based narrowing.
  • Open
    references/object-oriented-typing.md
    for metatable-backed class typing,
    self
    annotations, constructor return types, and exported instance aliases.
  • Open
    references/type-functions.md
    for analysis-time type computation, available libraries, and when advanced type-level transforms are justified.
  • Open
    references/roblox-types-in-luau.md
    for Roblox class, datatype, enum, constructor, service, and
    IsA
    typing behavior.
  • Do not open other skill references unless the request clearly crosses skill boundaries.
  1. 首先明确文件的类型检查模式预期:
    • --!strict
      用于需要强类型推导和早期错误检测的场景
    • --!nonstrict
      用于过渡阶段的代码,避免未解析的值产生过多噪音
    • 仅当任务明确要求关闭类型分析时才使用
      --!nocheck
  2. 在大面积添加注解之前优先保留有用的类型推导。仅在需要明确意图、稳定模块契约、约束
    self
    类型、防止类型意外拓宽为
    any
    时添加注解
  3. 对于复用的类型结构优先使用具体别名:
    • 结构化数据使用记录类型
    • 字典使用索引类型
    • 数组使用
      {T}
      格式
    • 模块对外契约使用导出别名
  4. 有意识地使用可选类型、联合类型和交叉类型:
    • T?
      等价于
      T | nil
    • 状态机或类结果类型使用标签联合类型
    • 合并兼容的表能力或函数签名使用交叉类型
  5. ::
    类型转换当作精度调整工具,而不是类型检查绕过手段。仅用它来收窄过于宽泛的推导类型,不要用它来掩盖不相关的类型错误
  6. 围绕类型关联设计泛型:
    • 在转换过程中保留元素类型
    • 在容器中保留键值类型关联
    • 当类型参数可以表达意图时,不要默认使用
      any
  7. 按照Luau的分析规则为表添加类型:
    • 未密封的表可以在本地添加字段
    • 加了注解或被返回的表会被密封
    • 宽度子类型化适用于密封的记录类型
  8. 对于类对象模块,将实例数据和类行为分离,从
    setmetatable
    推导实例类型,当方法需要共享类类型时显式注解
    self
  9. 保持模块API接口的类型安全:
    • 为面向消费者的数据导出命名别名
    • 实现细节保持私有
    • 选择能够清晰推导调用方类型的函数签名
  10. 仅在注解和类型细化时使用Roblox类型知识,例如
    Instance
    Part
    Enum.Material
    、数据类型和基于
    IsA
    的类型收窄

Checklist

参考文档

  • The chosen type-checking mode matches the maintenance goal of the file.
  • Public module contracts are explicit where reuse matters.
  • Inference is preserved where it remains precise.
  • any
    is avoided unless intentionally opting out.
  • Tables are typed according to their actual shape and sealing behavior.
  • Unions, intersections, and optionals reflect real states instead of vague catch-all types.
  • Generic parameters encode input/output relationships that callers rely on.
  • Method
    self
    typing is explicit where Luau cannot safely infer the shared class type.
  • Roblox types are used only to improve typing, not to drift into unrelated Roblox architecture.
  • No general syntax tutorial, performance advice, or networking/data/cloud guidance is included.
  • 打开
    references/type-system-overview.md
    查看文件模式、结构化类型、注解、类型转换、模块边界相关指引
  • 打开
    references/basic-types-and-table-typing.md
    查看内置类型、
    any
    unknown
    等特殊类型、函数签名、表状态、索引类型相关内容
  • 打开
    references/generics.md
    查看泛型别名、泛型函数、别名默认值、保留类型推导的容器模式相关内容
  • 打开
    references/unions-and-intersections.md
    查看结果类型结构、标签联合类型、判别符、安全使用交叉类型相关内容
  • 打开
    references/refinements.md
    查看真值检查、
    type(...)
    守卫、等值收窄、复合条件、基于
    assert
    的收窄相关内容
  • 打开
    references/object-oriented-typing.md
    查看基于元表的类类型定义、
    self
    注解、构造函数返回类型、导出实例别名相关内容
  • 打开
    references/type-functions.md
    查看分析阶段类型计算、可用库、适用高级类型转换的场景相关内容
  • 打开
    references/roblox-types-in-luau.md
    查看Roblox类、数据类型、枚举、构造函数、服务、
    IsA
    类型行为相关内容
  • 除非请求明确跨技能边界,否则不要打开其他技能的参考文档

Common Mistakes

检查清单

  • Leaving a variable unannotated in
    --!nonstrict
    and unintentionally turning it into
    any
    .
  • Replacing a useful generic relationship with
    any
    or an overly broad union.
  • Sealing a table too early with an annotation, then expecting to add fields later.
  • Expecting method definitions with
    :
    to automatically share a precise
    self
    type across the whole class.
  • Using
    ::
    to force unrelated conversions instead of fixing the underlying type design.
  • Building unions without a discriminant, then making downstream refinement difficult.
  • Using intersections between incompatible primitives such as
    string & number
    .
  • Mixing runtime Roblox architecture guidance into a type-only task.
  • 选择的类型检查模式符合文件的维护目标
  • 公共模块契约在需要复用的地方是显式定义的
  • 类型推导在保持精确的场景下被保留
  • 除非主动选择退出类型检查,否则避免使用
    any
  • 表的类型符合其实际结构和密封行为
  • 联合类型、交叉类型、可选类型反映真实的状态,而非模糊的通用类型
  • 泛型参数编码了调用方依赖的输入输出类型关联
  • 在Luau无法安全推导共享类类型的地方,方法的
    self
    类型是显式注解的
  • Roblox类型仅用于优化类型定义,没有偏离到不相关的Roblox架构内容
  • 没有包含通用语法教程、性能建议、网络/数据/云服务相关指引

Examples

常见错误

Export a stable module contract

luau
--!strict

export type User = {
    id: number,
    name: string,
    nickname: string?,
}

local M = {}

function M.makeUser(id: number, name: string): User
    return {
        id = id,
        name = name,
        nickname = nil,
    }
end

return M
  • --!nonstrict
    模式下不给变量加注解,意外让它变成
    any
    类型
  • any
    或过于宽泛的联合类型替代有用的泛型关联
  • 过早用注解密封一个表,之后又想要添加新字段
  • 期望用
    :
    定义的方法会自动在整个类中共享精确的
    self
    类型
  • 使用
    ::
    强制不相关的类型转换,而不修复底层的类型设计
  • 构建不带判别符的联合类型,导致后续的类型细化变得困难
  • 在不兼容的基础类型之间使用交叉类型,例如
    string & number
  • 在仅涉及类型的任务中混入运行时Roblox架构指引

Preserve relationships with a generic function

示例

导出稳定的模块契约

luau
--!strict

local function first<T>(items: {T}): T?
    return items[1]
end

local a = first({1, 2, 3}) -- number?
local b = first({"x", "y"}) -- string?
luau
--!strict

export type User = {
    id: number,
    name: string,
    nickname: string?,
}

local M = {}

function M.makeUser(id: number, name: string): User
    return {
        id = id,
        name = name,
        nickname = nil,
    }
end

return M

Refine a tagged union

使用泛型函数保留类型关联

luau
--!strict

type Loading = { kind: "loading" }
type Ready<T> = { kind: "ready", value: T }
type Failed = { kind: "failed", message: string }
type State<T> = Loading | Ready<T> | Failed

local function readValue(state: State<number>): number?
    if state.kind == "ready" then
        return state.value
    end

    return nil
end
luau
--!strict

local function first<T>(items: {T}): T?
    return items[1]
end

local a = first({1, 2, 3}) -- number?
local b = first({"x", "y"}) -- string?

Type an object-like module with explicit
self

细化标签联合类型

luau
--!strict

local Counter = {}
Counter.__index = Counter

type CounterData = {
    value: number,
}

export type Counter = typeof(setmetatable({} :: CounterData, Counter))

function Counter.new(initialValue: number): Counter
    return setmetatable({
        value = initialValue,
    }, Counter)
end

function Counter.increment(self: Counter, amount: number): number
    self.value += amount
    return self.value
end

return Counter
luau
--!strict

type Loading = { kind: "loading" }
type Ready<T> = { kind: "ready", value: T }
type Failed = { kind: "failed", message: string }
type State<T> = Loading | Ready<T> | Failed

local function readValue(state: State<number>): number?
    if state.kind == "ready" then
        return state.value
    end

    return nil
end

为带显式
self
的类对象模块添加类型

luau
--!strict

local Counter = {}
Counter.__index = Counter

type CounterData = {
    value: number,
}

export type Counter = typeof(setmetatable({} :: CounterData, Counter))

function Counter.new(initialValue: number): Counter
    return setmetatable({
        value = initialValue,
    }, Counter)
end

function Counter.increment(self: Counter, amount: number): number
    self.value += amount
    return self.value
end

return Counter