Quick doubt about type cheking

Hello!
Ok, this is just a quick doubt:
Can someone explain me why luau type cheking says that this is bad.
image
WeldC.Parent = hit
and this is bad:
WeldC.Parent = hit::Instance
image
But this is not:
WeldC.Parent = (hit::BasePart)::Instance

This is almost certainly a bug. However, I was unable to reproduce this on my own. Can you try to recreate the error with as little code as possible?

1 Like

This is an TYPE assertion in luau.

In this case, hit is directly assigned to WeldC.Parent. If hit is inferred or already known to be of a compatible type (e.g., Instance), this assignment is straightforward and type-safe.

WeldC.Parent = hit::Instance

This means that you are saying that hit is an Instance. However, if the type checker had earlier inferred hit as an even more specific kind such as BasePart, then it would be deemed redundant by the type checker to assert it again as an instance.

WeldC.Parent = (hit::BasePart)::Instance

This statement performs a nested type assertion
First, it asserts that hit is of type BasePart.
Then, it asserts that the resulting BasePart is of type Instance.

im a sleepy head

1 Like

Same as @OptimisticSide, I cannot reproduce this.

If you are using Roblox LSP, Luau LSP, or some other similar in nature, I’ve found that it does not fully match the behavior of Roblox’s built-in script analysis.

1 Like

I think im not getting you:
it says that hit::Instance is wrong becaues “types are unrelated” (and hit type is BasePart). So I don’t understand casting a BasePart as Instance is not Ok, but casting a BasePart as a BasePart and then to a Instance is Ok.

Is it weird, im not able to recreate it:
All I know is that this is bad:

local HookTouched = Hook.Touched:Connect(function(hit)
		if CanNotTouch[hit.Name] then return end
		CanNotTouch[hit.Name] = true
		if hit.Parent == GameFolder.Fountain.ToRemove then
			LeafsToRemove-=1
			hit.Anchored = false
			hit.CanCollide = false
			hit.CanTouch = false
			local WeldC = Instance.new("WeldConstraint")
			WeldC.Parent = hit
			WeldC.Part0 = hit
			WeldC.Part1 = Hook
			table.insert(HookedParts, hit)
			UpdateLeafsAmount()
			SoundFolder.LeafCollect.PitchShiftSoundEffect.Octave = math.random(90,110)/10
			SoundFolder.LeafCollect:Play()
			local Part:BasePart&{ParticleEmitter:ParticleEmitter}
			if InstantiatedParticleParts[1] then
				Part = InstantiatedParticleParts[1]
				table.remove(InstantiatedParticleParts,1)
			else
				Part = script.ParticlesPart:Clone()
			end
			Part.Parent = workspace
			Part.CFrame = hit.CFrame
			Part.ParticleEmitter:Emit(10)
			wait(1.5)
			Part.Parent = script
			table.insert(InstantiatedParticleParts, Part)
		end
	end)

And this is not:

--!strict
local Part:BasePart = script.Parent

local a:{BasePart} = {}

local HookTouched = Part.Touched:Connect(function(hit)
	if hit.Parent ~= workspace then
		hit.Anchored = false
		hit.CanCollide = false
		hit.CanTouch = false
		local WeldC = Instance.new("WeldConstraint")
		WeldC.Parent = hit
		WeldC.Part0 = hit
		WeldC.Part1 = Part
		table.insert(a, hit)
	end
end)



(Both scripts have the “--!strict” at top.)

Let me rephrase, you did not state it you were in strict mode at the start of your post.
So since you clarified that you did use !strict in your code it makes sense.

When in !strict It enforces strict checking to identify possible type errors.

First Example Uses ‘DIRECT’ method

BasePart belongs to the subclass of Instance. However, the type checker doesn’t support direct type assertion from a specific subtype like BasePart to a supertype like Instance.

This limitation exists to prevent unsafe type conversions that could violate important type constraints.

Second One Uses ‘Subtype Casting Directly’ method

Similar to the first case, the type checker prevents direct casting of a BasePart to an Instance because it considers this as an unsafe type conversion.

This ensures that you explicitly handle such conversions.

And the third one uses ‘Nested’ method

By first asserting hit as BasePart and then as Instance, you are explicitly stating the type conversion path. This process makes it so that each type conversion is valid and safe.

The type checker recognizes this explicit conversion path and allows it.

Simple reason why this occurs in !strict

  • Directly asserting BasePart as Instance could hide bugs or type mismatches.
    The nested assertion makes the conversion process clear, making sure each step is checked.

  • The type checker needs to be certain that each type conversion is safe.
    By breaking it down into smaller steps
    you ensure that each conversion is checked for correctness.

  • This would potentially make your code more readable & maintainable
    by clearly stating each type conversion.

The behavior difference is because the type checker requires clear and safe type conversions.

Direct assertions or casts from BasePart to Instance are not allowed to prevent errors.
Nested assertions provide a clear and safe conversion path.
Which meet the type checker’s requirements

2 Likes

Ok, thanks I got it now.
(Is there any doc category about unsafe conversions?)

1 Like

You can go here

Or use the devforum topics

Hope this helps you in someway.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.