TypeChecking/Intellisense for a table of required modules?

I have a situation where I have a folder containing a bunch of utility modules (each returning arrays containing various functions and/or useful variables:
image

I would like to require the ‘MainModuleScript’ and have it combine all of the individual utility modules into a single dictionary that can be referenced by multiple other scripts within the game:

local TableOfModules: {
	[string]: any;	
} = {};

local UtilitiesFolder = script:WaitForChild('Utilities');

for _, UtilityModule in ipairs(UtilitiesFolder:GetChildren()) do
	if (UtilityModule:IsA('ModuleScript')) then
		local Utility = require(UtilityModule);
		TableOfModules[UtilityModule.Name] = Utility :: typeof(Utility);
	end
end

return TableOfModules;

My issue is with the intellisense not being able to infer the types of each module. Ideally I’d like to be able to go:

TableOfModules.Utility1 -- And have intellisense provide me with information/suggestions relating to the information from that utility module

The other way of doing this would be to individually define each utility module manually:

local Utility1 = require(UtilityFolder:WaitForChild('Utility1'));
local Utility2 = require(UtilityFolder:Wa... etc.

local TableOfModules = {
    ['Utility1'] = Utility1 :: typeof(Utility1);
    ['Utility2'] = Utility2 :: typeof(... etc.
};

Which does work, but this seems very cumbersome especially considering as my real application is currently up to about 15 unique utility modules, and it’s annoying to have to append my table every time I add a new one. I’d appreciate any suggestions.

1 Like

Use rojo and typescript if you rely on types. luau has minimal implementations, your casting the same table as your actual value. which is just useless. I dont asee a reply. and i though maybe you should know this.

1 Like

A workaround for this could be like:

type Function<A, T> = (A) -> T;

type ModuleFunction =
    & Function<"Utility1", typeof(Utility1)> --// Utility1 = required module
    & Function<"Utility2", typeof(Utility2)> --// Utility2 = required module
    & Function<"Utility3", typeof(Utility3)> --// Utility3 = required module;

--// Implementation
MainModule.GetUtility = function(Name: string)
    
    --// Try to find the module
    local Module = Utilities:FindFirstChild(Name);
    
    --// Check if the module exists
    assert(Module, "A valid utility name must be provided.");
    
    --// Return the required module
    return require(Module);
end :: ModuleFunction

Good suggestion, though I probably won’t be switching up my workflow for this project. Just wanted to know whether the it’s a limitation of studio and/or whether there’s any decent workarounds. Also the casting was a leftover from when I was messing around with it trying to find a hacky workaround. Thanks for the response though.

Interesting suggestion, although my intention was to find a way of avoiding having to individually write out the modules. As far as I can tell I’d still have to define the parameter/return for each module in the ModuleFunction type, which is of similar effort to simply explicitly defining them in a table. Unless I’ve misread your code.