t Runtime Type Checker for Roblox

repository·master·Indexed 43 days ago

https://github.com/osyrisrblx/t

A runtime type checker for Roblox (version 3.2.1) that allows developers to define complex type schemas for primitives, tables, interfaces, and Roblox-specific objects like Instances and Enums. It provides tools for validating data integrity, preventing exploits via Remote objects, and wrapping functions with automatic type validation using t.wrap.

Tokens
5.3K
Snippets
19
Records
23
Agent score
6%

What's inside t

  1. How type composition works

    master

    You can combine existing type checkers to create more complex requirements. Most composition functions take a type checker as an argument and return a new one.

    Example: t.optional(t.string) creates a checker that passes if the value is either a string or nil.

    local mightBeAString = t.optional(t.string)
    print(mightBeAString("Hello")) --> true
    print(mightBeAString()) --> true
    print(mightBeAString(1)) --> false, "(optional) string expected, got number"
  2. How to validate arrays

    master

    In Lua, arrays are tables with sequential integer keys. Use t.array(check) to ensure a table is a valid array and that every value in that array matches the provided check.

    t.array(t.string) -- Validates a table where all values are strings and keys are sequential integers
  3. Create custom type checkers

    master

    A custom type checker is simply any function that returns a boolean (or boolean, string for error messages). This allows you to integrate t with custom OOP frameworks or complex logic.

    Example of a class instance checker:

    local function instanceOfClass(class)
    	return function(value)
    		local tableSuccess, tableErrMsg = t.table(value)
    		if not tableSuccess then
    			return false, tableErrMsg or ""
    		end
    
    		local mt = getmetatable(value)
    		if not mt or mt.__index ~= class then
    			return false, "bad member of class"
    		end
    
    		return true
    	end
    end
    local MyClass = {}
    MyClass.__index = MyClass
    
    function MyClass.new()
    	return setmetatable({}, MyClass)
    end
    
    local function instanceOfClass(class)
    	return function(value)
    		local tableSuccess, tableErrMsg = t.table(value)
    		if not tableSuccess then
    			return false, tableErrMsg or ""
    		end
    
    		local mt = getmetatable(value)
    		if not mt or mt.__index ~= class then
    			return false, "bad member of class"
    		end
    
    		return true
    	end
    end
    
    local instanceOfMyClass = instanceOfClass(MyClass)
    local myObject = MyClass.new()
    print(instanceOfMyClass(myObject)) --> true
  4. Use t for runtime type checking

    master

    The t module allows you to define type schemas and validate values against them. This is useful for ensuring function arguments are correct and for validating data received from Remote objects to prevent exploits or server errors.

    To use it, define a check (often using t.tuple for multiple arguments) and use it within an assert or directly to validate data.

    local t = require(path.to.t)
    
    local fooCheck = t.tuple(t.string, t.number, t.optional(t.string))
    local function foo(a, b, c)
    	assert(fooCheck(a, b, c))
    	-- you can now assume:
    	--	a is a string
    	--	b is a number
    	--	c is either a string or nil
    end
    
    foo("1", 2, "3") -- Valid
    foo("1", 2, 3)   -- Error: Bad tuple index #3: (optional) string expected, got number
  5. Define and validate interfaces

    master

    Interfaces allow you to define the required structure of a table.

    t.interface(definition) Creates a checker based on a table of type checkers. Fields do not need to be present unless specified (use t.optional for clarity, though standard interfaces in t allow extra fields).

    t.strictInterface(definition) Similar to t.interface, but will fail if the table contains any fields not defined in the definition.

    Example:

    local IPlayer = t.interface({
    	Name = t.string,
    	Score = t.number,
    })
    
    local myPlayer = { Name = "TestPlayer", Score = 100 }
    print(IPlayer(myPlayer)) --> true
    local IPlayer = t.interface({
    	Name = t.string,
    	Score = t.number,
    })
    
    local myPlayer = { Name = "TestPlayer", Score = 100 }
    print(IPlayer(myPlayer)) --> true
    print(IPlayer({})) --> false, "[interface] bad value for Name: string expected, got nil"
    
    -- Strict interface example
    local IPlayerStrict = t.strictInterface({
    	Name = t.string,
    	Score = t.number,
    })
    print(IPlayerStrict({ Name = "Test", Score = 1, A = 1 })) --> false, "[interface] unexpected field 'A'"
  6. Wrap functions with type checks

    master

    Instead of manually calling assert(check(args)) inside every function, you can use t.wrap to create a version of your function that automatically performs type validation on its arguments.

    t.wrap(callback, argCheck) Takes a function and a type checker (like t.tuple) and returns a new function that runs the check before executing the callback.

    local fooCheck = t.tuple(t.string, t.number, t.optional(t.string))
    
    -- Instead of this:
    local function foo(a, b, c)
    	assert(fooCheck(a, b, c))
    	print(a, b, c)
    end
    
    -- Do this:
    local foo = t.wrap(function(a, b, c)
    	print(a, b, c)
    end, fooCheck)
    
    -- Or use t.strict to wrap the check itself:
    local foo = t.strict(fooCheck)(function(a, b, c)
    	print(a, b, c)
    end)
  7. Reference: Roblox Instance type checkers

    master

    Use these functions to validate Roblox Instances and their hierarchies.

    • t.instanceOf(className[, childTable]): Ensures the value is an Instance with the exact className. If childTable is provided, it validates children using t.children().
    • t.instanceIsA(className[, childTable]): Ensures the value is an Instance that matches className via IsA comparison.
    • t.children(checkTable): Validates an instance's children against a table where keys are child names and values are type checkers.

    Warning: If an instance tree has more than one child with the same name, t.children will always return false.

  8. Reference: Special string functions

    master

    Use t.match(pattern) to check if a string matches a specific pattern using string.match(value, pattern).

    **`t.match(pattern)`**
    checks `t.string` and determines if value matches the pattern via `string.match(value, pattern)`
  9. Reference: Roblox Enum type checkers

    master

    Validate Roblox Enums and EnumItems.

    • t.Enum: Ensures the value is an Enum (e.g., Enum.Material).
    • t.EnumItem: Ensures the value is an EnumItem (e.g., Enum.Material.Plastic).
    • t.enum(enum): Passes if the value is an EnumItem belonging to the specific enum provided.
  10. Reference: Primitive type checkers

    master

    The following functions check for basic Lua and Roblox primitive types. Note that nil is mapped to t.none and function is mapped to t.callback due to Lua keyword restrictions.

    |Type     |  |Member     |
    |---------|--|-----------|
    |boolean  |=>|t.boolean  |
    |thread   |=>|t.thread   |
    |function |=>|t.callback |
    |nil      |=>|t.none     |
    |number   |=>|t.number   |
    |string   |=>|t.string   |
    |table    |=>|t.table    |
    |userdata |=>|t.userdata |
  11. Reference: Special number functions

    master

    Use these functions to validate numbers against specific ranges or properties.

    **General:**
    **`t.nan`**
    determines if value is `NaN`
    All of the following checks will not pass for `NaN` values.
    If you need to allow for `NaN`, use `t.union(t.number, t.nan)`
    
    **`t.integer`**
    checks `t.number` and determines if value is an integer
    
    **`t.numberPositive`**
    checks `t.number` and determines if the value > 0
    
    **`t.numberNegative`**
    checks `t.number` and determines if the value < 0
    
    **Inclusive  Comparisons:**
    **`t.numberMin(min)`**
    checks `t.number` and determines if value >= min
    
    **`t.numberMax(max)`**
    checks `t.number` and determines if value <= max
    
    **`t.numberConstrained(min, max)`**
    checks `t.number` and determines if min <= value <= max
    
    **Exclusive Comparisons:**
    **`t.numberMinExclusive(min)`**
    checks `t.number` and determines if value > min
    
    **`t.numberMaxExclusive(max)`**
    checks `t.number` and determines if value < max
    
    **`t.numberConstrainedExclusive(min, max)`**
    checks `t.number` and determines if min < value < max