Type errors for optional params objects

I am writing a data replication util that will likely need more arguments in the future so I deemed it a good idea to include a typed parameter object for confuguration of said replicator.
I keep encounering issues with type checking though and couldn’t find any posts about similar issues

function module.Create(tbl : {[any] : any}, creationParameters : CreationParams?)
	
	if not(creationParameters) then
		creationParameters = {} :: CreationParams
	end
	creationParameters = TableUtil.Inherit(creationParameters :: CreationParams, defaultCreationParams) :: CreationParams -- #1 Type CreationParams could not be converted into anyTable
	
	local observers = {} :: {[any] : {ObserveFunction}}
	
	local function fireObservers(index : any, newValue : any, oldValue : any, isInitialization : boolean, isDefaulting : boolean)
		local functions = observers[index]
		if functions then
			for _, f in pairs(functions) do
				if creationParameters.UseTaskSpawn then -- #2 Value of type CreationParams could be nil ?
					task.spawn(f, newValue, oldValue, isInitialization, isDefaulting)
				else
					task.defer(f, newValue, oldValue, isInitialization, isDefaulting)
				end
			end
		end
	end

The line with the #1 comment can’t convert CreationParams into anyTable which is {[any] : any?} which is strange because I assumed that anything could be converted into any. This error is fixed by converting it into CreationParams (again?) with :: why is that?

The line with #2 comment has a problem with the possibility of CreationParams being nil since it is an optional argument. Which is also strange because I convert it from CreationParams? to CreationParams at every single point :unamused:

1 Like

So problem 1 is due to the mismatch of types. In strict mode the type checker will check for exact types so a string is not the same as any, and you get the same error in this simplified example.

type x = {[string]:string}
type a = {[any]:any?}

local function giveAnyTable(t:x):a
	return t --Type Error: type pack 'x' could not be converted into 'a'...
end

local tX :x = {X = "x"}
local tAny :a = giveAnyTable(tX)
--Play around with the type assignments for variables or parameters and the error simply jumps around to wherever the mismatch occurs.

The second problem is related to the initial definition of the argument creationParameters being the type creationParams?. When you type cast with :: the engine checks that the type being cast is a valid member of the union creationParams | nil, it doesn’t actually remove the nil from the type union. Otherwise you could be asking it to accept this: local tY :y? = nil ::y
So in order to do this we have to create a situation where the code could never give a nil value and change the type to reflect that, such as by creating a new variable.

local cp :CreationParams = creationParameters or {}

However, if TableUtil.Inherit(...) returns a type of {[any]:any} then the resulting table will be a union of the types: {[any]:any} | CreationParams. So to avoid this you would need to ensure the function returns the CreationParams type only.
You could do this by using generic typing in the function so it can infer what type to return whilst not stating the types explicitly, allowing you to possibly use it for other purposes.

local function util<T, U>(a:T, b:U) :T
    local c :T = {}
    --combine a and b into c
    return c
end

Generic types do the trick! Thank you for the explanation.

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