Referencing Children in Strict Typechecking Mode

Hello.
I wish to begin using strict type checking as default in my future scripts.
I am having difficult with a particular nuance of strict type checking mode.

Consider the following:

--!strict
local function target(turret: Model, target: Vector3?)
    local base = turret.base
    local baseHinge = base.hinge
    local barrelsHinge = turret.barrels.hinge
    if target then
	    local offsetCFrame = CFrame.new(base.Position, players[target].Character.HumanoidRootPart.Position).LookVector 

I am receiving an error on on line 3 and 6 of this particular script when attempting to reference child instances ā€œbaseā€ and ā€œbarrelsā€, with the error message ā€œKey ā€˜Base/Barrelsā€™ not found in class ā€˜Modelā€™ā€.

Whilst Iā€™m aware that :FindFirstChild() is better practice, my understanding is that there is performance overhead associated with the function and itā€™s a lot less concise, so I donā€™t use it. But, in order to fix this issue, I tried to use it.

--!strict
local function target(turret: Model, target: Vector3?)
    local base = turret:FindFirstChild("base")
    local baseHinge = base:FindFirstChild("hinge")
    local barrelsHinge = turret:FindFirstChild("barrels"):FindFirstChild("hinge")
    if target then
	    local offsetCFrame = CFrame.new(base.Position, players[target].Character.HumanoidRootPart.Position).LookVector 

However, in this case the last line gives an error stating ā€œKey ā€˜Positionā€™ not found in class ā€˜Modelā€™ā€.

I also tried to force the type of base to be BasePart but this throws an error as well, stating that ā€œType ā€˜Instanceā€™ could not be converted into ā€˜BasePartā€™ā€.

I would greatly appreciate if someone could help diagnose this issue and point out what Iā€™m doing wrong.

Thanks.

Iā€™m aware the last line has another error associated with it, but Iā€™m going to fix that soon, after this code works.

This is one of the problems that can be a bit hard to get around with strict mode.

The reason this problem happens is that the type Model and the type Model with children are technically 2 different types and original type Model you use doesnā€™t account for children.

There are 2 ways you can get around this problem:

  1. Use intersection types to get typechecker recognize children like this:
type TurretModel = Model & {base: BasePart & {hinge: HingeConstraint}, barrels: BasePart & {hinge: HingeConstraint}}

local function target(turret: TurretModel, target: Vector3?)
	local base = turret.base
	local baseHinge = base.hinge
	local barrelsHinge = turret.barrels.hinge
	if target then
		local offsetCFrame = CFrame.new(base.Position, players[target].Character.HumanoidRootPart.Position).LookVector
  1. Call typeof on the turret template when declaring the type of the turret parameter and use union types with type Model to bypass the linter errors then redeclare turret as a local inside function with typeof turret model and type assertation operator (::) like this:
--!strict
local TurretModel = script.Model
type TurretModel = typeof(TurretModel)

local function target(turret: TurretModel | Model, target: Vector3?)
	local turret: TurretModel = turret :: TurretModel
	local base = turret.base
	local baseHinge = base.hinge
	local barrelsHinge = turret.barrels.hinge
	if target then
		local offsetCFrame = CFrame.new(base.Position, players[target].Character.HumanoidRootPart.Position).LookVector
5 Likes

Thanks.
Iā€™ve just decided to stay on non strict for the time being until Roblox makes the system more robust and approachable.

Yeah honestly, this wouldā€™ve been a lot more easier if generic functions and default type parameters were a thing right now combined with autocomplete as you only would have to do this:

--!strict
local TurretModel = script.Model
type TurretModel = typeof(TurretModel)

local function target<Turret = TurretModel>(turret: Turret, target: Vector3?)
	local base = turret.base
	local baseHinge = base.hinge
	local barrelsHinge = turret.barrels.hinge
	if target then
		local offsetCFrame = CFrame.new(base.Position, players[target].Character.HumanoidRootPart.Position).LookVector
--...
target<typeof(turret)>(turret, Vector.new(0, 1, 0)) --For example.