Get child of a model error with type checking?

I’m wanting to use type checking in my parameters, but it shows an error

local function PrintModelPart(model: Model)
	print(model.A)
end

local Model = Instance.new("Model")
local Part = Instance.new("Part")
Part.Name = "A"
Part.Parent = Model

PrintModelPart(Model)


Note, this works as intended, I just don’t like red/yellow lines in my code, especially when they clearly not an issue…

1 Like

In the function PrintModelPart you typecast model as specfically a Model. Type checking cannot assume anything more than a pure Model (in other words, it can only be sure of the properties, methods, and events of a Model class instance, but cannot infer anything about any children).

There is a method of (sort of) solving this, however it requires “prefabs” to exist instead of dynamic creation through code:

--Assume there is a Model named "MyModel" with a single child Part named "A" parented to the script this code is in
type MyModel = typeof(script.MyModel)
local function PrintModelPart(model: MyModel)
   print(model.A) --This should have code completion and no annoying squiggly lines!
end

--You can use this type for creational pattern functions as well:
local function createMyModel(): MyModel
   return script.MyModel:Clone()
end

--And later on still be able to use the type...
local newMyModel = createMyModel()
newMyModel.A.Color = Color3.new(1, 0, 0) --Again, this should have code completion and be perfectly valid in the eyes of the linter!

You can make your own type that includes the properties of a Model aswell as your own like this:

type myModel = Model & {
	A: Instance
} -- this is called an intersection, https://create.roblox.com/docs/luau/type-checking#unions-and-intersections

local function PrintModelPart(model: myModel)
	print(model.A) -- no more squiggly
end

I used to use this method as well, however I have had issues using it under --!strict and the beta type solver and have heard that it’s not an intended way to construct custom instance hierarchies.

1 Like

Oh really? That’s interesting… What issues have you had under --!strict (out of curiosity)?

Your method is also very interesting; I think each method has their own scope and risks.

1 Like

Just general annoyances with the linter giving me warnings when I don’t want it to. Here’s an example of a few pain points:

--!strict

type CustomFolder = Folder & {
	PartA: Part
}

local function createCustomFolder(): CustomFolder
	
	local folder = Instance.new("Folder")
	local partA = Instance.new("Part")
	partA.Name = "PartA"
	
	return folder --Type error under strict mode
	
end

--Assume there is a Folder (named "CustomFolder") in workspace that matches what you'd expect from the type CustomFolder
local workspaceCustomFolder: CustomFolder = workspace.CustomFolder --Type error under strict mode
local worksWithoutTypeAssignThough = workspace.CustomFolder --No type error here!

local function takesCustomFolder(arg: CustomFolder)
end

takesCustomFolder(workspaceCustomFolder) --No type error here!
takesCustomFolder(worksWithoutTypeAssignThough) --Type error under strict mode
1 Like

Huh, interesting… it gives out the error that not all intersection parts are compatible – wonder why?

image

If it would interest you, I actually found a way to fix the error under strict mode – type casting seems to do the trick.

--!strict

type CustomFolder = Folder & {
	PartA: Part
}

local function createCustomFolder(): CustomFolder
	local folder = Instance.new("Folder")
	local partA = Instance.new("Part")
	partA.Name = "PartA"

	return (folder :: CustomFolder) -- no more type error, still works as intended
end
1 Like

Yeah, casting can sometimes fix the issue, but as the codebase grows the amount of places that you need to type cast gets to be ridiculous (that is, as long as you can avoid the dreaded “Cannot cast … the types are unrelated” type error!).

I’m pretty sure the type errors are technically (albeit annoyingly so) correctly reported in most cases, and it likely has more to do with a flaw in the design of creating types in that manner / using strict mode where it should not be used.

1 Like

That’s really interesting, thanks for the info btw.

1 Like