Luau typechecking: Intersect modulescript return values when using GetChildren

Right now, if you call GetChildren or GetDescendants on an Instance, it will return an Instance table irrespective of the actual hierarchy contents. While this behavior supports objects whose children can’t be anticipated at runtime, it loses important information on hierarchies that don’t change.

A particularly annoying example is ModuleScripts. Consider the following modules and their return values:

a -> { init: ()->(), start: ()->() }
b -> { start: ()->(), dontStart: boolean }

If you put these modules into a Folder, and then call GetChildren on the Folder, you will receive {Instance}. This is bad, because now if I want to use my modules I have to cast them into any, which defeats the whole point of using the feature.

Instead, that GetChildren call should return an array of an Instance type with particular metadata. To illustrate:

Instance, where ModuleScripts return
   { init: ()->()?, start: ()->(), dontStart: boolean? }

Indexing the child list would now return an object of that type, with that special metadata. Refining the type with IsA('ModuleScript') would refine it to:

ModuleScript, which returns
   { init: ()->()?, start: ()->(), dontStart: boolean? }

At which point the user could write something like this, completely compliant with luau typechecking:

for _, module in modules:GetChildren() do
   if module:IsA('ModuleScript') then
      local library = require(module)
      if library.init then
         library.init()
      end
      if not library.dontStart then
         library.start()
      end
   end
end

I need this because I use module loaders. My module hierarchy is not static - I reorganize things constantly and am not interested in specifying their new locations every time something moves, or gets added or deleted.

4 Likes