Feedback on type validation function

I wrote this function to check if the values being sent of remote events were the correct type. But I can’t help but think there is a better of more efficient way of doing this.

Here is my code:

--!strict

type TypeValidator = (...any) -> (boolean, string?)

type ReturnType = {
    strict: (checkFor: string) -> (ReturnType),
    optional: (checkFor: string) -> (ReturnType),
    construct: () -> (TypeValidator)
}

return function(): ReturnType
    local typeCheckers: { [number]: TypeValidator } = {}
    local methods
    local minArguments = 0
    local maxArguments = 0

    local function check(...: any): (boolean, string?)
        local arguments = select("#", ...)

        if arguments < minArguments or arguments > maxArguments then
            return false, string.format(
                "Expected %d arguments, got %d.",
                arguments < minArguments and minArguments or maxArguments,
                arguments
            )
        end

        for i = 1, arguments do
            local argument = select(i, ...)
            local success: boolean, errorMessage: string? = typeCheckers[i](argument)

            if not success then
                return false, errorMessage
            end
        end

        return true
    end

    local function strict(checkFor: string): ReturnType
        minArguments = minArguments + 1
        maxArguments = maxArguments + 1

        table.insert(typeCheckers, function(value: any)
            if checkFor == "any" then
                return true
            end

            local gotType = typeof(value)
            if gotType == checkFor then
                return true
            else
                return false, string.format("Expected %q, got %q.", checkFor, gotType)
            end
        end)

        return methods :: ReturnType
    end

    local function optional(checkFor: string): ReturnType
        maxArguments = maxArguments + 1

        table.insert(typeCheckers, function(value)
            if checkFor == "any" or value == nil then
                return true
            end

            local gotType = typeof(value)
            if gotType == checkFor then
                return true
            else
                return false, string.format("Expected %q or \"nil\", got %q.", checkFor, gotType)
            end
        end)

        return methods :: ReturnType
    end

    local function construct(): (...any) -> (boolean, string?)
        return check
    end

    methods = {
        strict = strict,
        optional = optional,
        construct = construct
    }

    return methods
end

Here is a sample use case:

local TypeValidator = require(path.to.module)
local CheckType = TypeValidator().strict("boolean").optional("number").construct()

RemoteEvent.OnServerEvent:Connect(function(a: boolean, b: number)
    local success: boolean, errorMessage: string? = CheckType(a, b)
    if not success then
        error(errorMessage :: string, 2)
    end
    --do something
end)
2 Likes