Weird type error

Type ‘{(unknown, unknown, { coins: a }) → boolean}’ could not be converted into ‘{(Player, ReceiptInfo, DefaultData) → boolean}’; at indexResult().arguments()[0], unknown is not exactly Player at indexResult().arguments()[1], unknown is not exactly ReceiptInfo at indexResult().arguments()[2], { coins: a } is not exactly DefaultData type {(unknown, unknown, { coins: a }) → boolean}.indexResult().arguments().tail() (…any) is not exactly {(Player, ReceiptInfo, DefaultData) → boolean}.indexResult().arguments() (Player, ReceiptInfo, DefaultData)

type ReceiptInfo = {
    PlayerId: number,
    ProductId: number,
    PurchaseId: string
}

type ProductCallback = (player: Player, receiptInfo: ReceiptInfo, data: DataManager.DefaultData) -> boolean

type ProductHandlerImpl = {
    processReceipt: (receiptInfo: ReceiptInfo) -> Enum.ProductPurchaseDecision,
    ProductCallbacks: {[number]: ProductCallback}
}

local ProductHandler = {} :: ProductHandlerImpl

ProductHandler.ProductCallbacks = {
    [12345] = function(player, receiptInfo, data)
        data.coins += 100
        return true
    end
}

it’s talking abt the function in the ProductCallbacks table

It seems to shut up when I literally put ProductCallbacks in the ProductHandler table. However, I don’t seem to understand why that would be the fix?

Just add type annotations to the parameters: player : Player, receiptInfo: ReceiptInfo etc because currently it doesn’t know the types of these so it marks them as unknown and unknown doesn’t match the type of the callback.

type ReceiptInfo = {
    PlayerId: number,
    ProductId: number,
    PurchaseId: string
}

type ProductCallback = (player: Player, receiptInfo: ReceiptInfo, data: DataManager.DefaultData) -> boolean

type ProductHandlerImpl = {
    processReceipt: (receiptInfo: ReceiptInfo) -> Enum.ProductPurchaseDecision,
    ProductCallbacks: {[number]: ProductCallback}
}

local ProductHandler = {} :: ProductHandlerImpl

ProductHandler.ProductCallbacks = {
    [12345] = function(player, receiptInfo, data: DataManager.DefaultData)
        data.coins += 100
        return true
    end
} :: {[number]: ProductCallback}

Doing this fixes the error, but it doesn’t make any sense, I already should be already do this when I write local ProductHandler = {} :: ProductHandlerImpl right?

No, you cast the array to a producthandlerimpl so the type analyser expects two properties one named productcallbacks, okay inside of it the analyser expects only fns of type product callback, so if you try to access from productcallbacks it will give you intellisense for that type, but trying to assign an fn not of product callback will error because even though the array is of type product callback, the function isn’t. I think it would’ve worked normally as well if you cast the fn to product callback but idk.

You can easily test this by replicatin the case with a simplified example:

--!strict
local ins : Instance = Instance.new("Part")
ins.ClassName --when typing the . Aka reading you get intellisense for instance

ins = 5--the type analyser complains, because even though ins is of type Instance, 5 isn't.

Hope this helps, I’m bad at explaining things. I’m hoping the example carries this explanation.

Edit: type annotations specify what type it expects to be there, but that doesn’t mean whatever you assign to it will suddenly be of that type.

1 Like

Ah I see, so luau will treat function parameters as unknown unless I explicitly write it. So, what you said is the solution. However, I am just wondering if there is a way I can make it so that I don’t have to keep writing the types of each callback if I have multiple of them.

As far as I know there isn’t, but you can turn off strict or cast every fn to ProductCallback if you don’t want to specify parameter types.

Going to keep this without a proper solution to see if someone can figure out a better alternative.

Why don’t you want to write the types out for each parameter? That’s the best solution to your problem that doesn’t involve any sort of weird or unnecessary casting, and you should be annotating parameters for all functions regardless when using types.

ProductHandler.ProductCallbacks = {
	[12345] = function(player: Player, receiptInfo: ReceiptInfo, data: DataManager.DefaultData)
		data.coins += 100
		return true
	end
} -- Fine