Strict - Simplified runtime type checking

This is a simple runtime type checker that works for modules.
Here’s a test script that will allow you to test and see how module works:

local StrictModule = require(game:GetService("ReplicatedStorage"):WaitForChild("strict"))

-- Define a module with functions to test
local TestModule = {}

-- Function 1: Add two numbers
function TestModule.addNumbers(a, b)
	return a + b
end

-- Function 2: Greet a player (with optional greeting argument)
function TestModule.greetPlayer(player, greeting)
	greeting = greeting or "Hello"
	return greeting .. ", " .. player.Name
end

-- Function 3: Teleport a player to a part (optional destination)
function TestModule.teleportPlayer(player, destination)
	destination = destination or workspace:FindFirstChild("SpawnLocation") or workspace:WaitForChild("Part")
	if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
		player.Character.HumanoidRootPart.CFrame = destination.CFrame
	end
end

-- Wrap the module with strict type checking (module table is frozen after that)
TestModule = StrictModule({
	addNumbers = {
		func = TestModule.addNumbers,
		types = { "number", "number" } -- Both arguments must be numbers
	},
	greetPlayer = {
		func = TestModule.greetPlayer,
		types = { "Player", "string?" } -- First argument must be a Player, second is optional string
	},
	teleportPlayer = {
		func = TestModule.teleportPlayer,
		types = { "Player", "BasePart?" } -- First argument must be a Player, second is optional BasePart
	}
})

-- Test the functions
local player = game.Players.LocalPlayer

-- Test 1: addNumbers (correct usage)
print("Test 1 (Correct):", TestModule.addNumbers(5, 10)) -- Output: 15

-- Test 2: addNumbers (incorrect usage)
local success, err = pcall(function()
	TestModule.addNumbers(5, "10") -- Error: Invalid type for argument 2. Expected number, got string.
end)
if not success then
	print("Test 2 (Error):", err)
end

-- Test 3: greetPlayer (correct usage)
print("Test 3 (Correct):", TestModule.greetPlayer(player)) -- Output: Hello, [PlayerName]
print("Test 3 (Correct):", TestModule.greetPlayer(player, "Hi")) -- Output: Hi, [PlayerName]

-- Test 4: greetPlayer (incorrect usage)
local success, err = pcall(function()
	TestModule.greetPlayer("NotAPlayer") -- Error: Invalid type for argument 1. Expected Player, got string.
end)
if not success then
	print("Test 4 (Error):", err)
end

-- Test 5: teleportPlayer (correct usage)
local part = workspace:FindFirstChild("Part") or Instance.new("Part", workspace)
part.Name = "Part"
part.Position = Vector3.new(0, 10, 0)

TestModule.teleportPlayer(player, part) -- Teleports player to the part
print("Test 5 (Correct): Player teleported to part.")

-- Test 6: teleportPlayer (incorrect usage)
local success, err = pcall(function()
	TestModule.teleportPlayer("NotAPlayer", part) -- Error: Invalid type for argument 1. Expected Player, got string.
end)
if not success then
	print("Test 6 (Error):", err)
end

-- Test 7: teleportPlayer (optional argument)
TestModule.teleportPlayer(player) -- Teleports player to default destination (SpawnLocation or Part)
print("Test 7 (Correct): Player teleported to default destination.")
2 Likes

Why use this module when you can just define the types for parameters in the functions themselves:

function TestModule.addNumbers(a: number, b: number): number
	return a + b
end

Does your module provide any advantages over the regular method of typing?

can you please read that its realtime type checking, not in script editor one.

Yes I know that the difference is that it produces errors when a type is wrong, but isn’t a warning in the script enough to let you know something is wrong?

The only time I’d see an error for a wrong type being necessary is if the passed parameter type isn’t known at runtime so a check is required, and a warning is probably the better option unless the following code will have a knock-on effect like accessing a datastore.

Its rather for security than to know something is wrong by the way;
Search type script you will get what i am talking about and why its needed

From what I can see, TypeScript is pretty similar to --!strict mode for Luau. Sure, there are cases where you’ll want to confirm types like I mentioned, another example being remote events. But my point is what advantage does your module provide over this:

function TestModule.addNumbers(a: number, b: number): number
	if (type(a) ~= "number" or type(b) ~= "number") then
		error(`The expected parameters for addNumbers were 'number, number', got {type(a), type(b)}`)
	end
        
	return a + b
end

I don’t mean to undermine your effort since you put time into this and I might come across as blunt, but what makes this module stand out from the regular methods of type checking?

1 Like

ugh, i would pour some hate on not using assert() right here:
if (type(a) ~= "number" or type(b) ~= "number") then
But since you’re a modeler fine.
I made this module to not code each assert individually.

The reason I don’t use assert is because when you define an assert function, any string concatenation in the second parameter is done whether the condition is true or false, where as using an if statement only does the string concatenation when the condition fails.

Having a utility module to save repeating yourself isn’t a bad idea, but it seems like your solution is a bit more cumbersome than just putting in the checks.

fair enough
robloxrobloxrbolox

Would you have seen the “t” module for realtime type checking… its so overcomplicated

Yeah that does seem overcomplicated but that’s from 2018, I don’t think strict typing was implemented back then.

Also keep sharing resources, I don’t want to discourage you. :+1:

Doesn’t really make a difference in most cases; concatenation is fast. (The result will be cached anyway)