Luau type checking for OOP proxy tables

Instead of returning an object in my module:New() function I return a proxy table that internally indexes and writes to the object. The issue is that in my module:Delete() method i set self._destroyed = true.

Luau thinks that I am trying to write to the proxy table and not the object table behind it.

I’ve tried to use self()._destroyed = true to use the metamethod __call but that doesn’t work either.

--!strict

local HttpService = game:GetService("HttpService")

local Style = require(script.Parent.Parent:WaitForChild("Style"))
local BaseStyle = Style:New()

type Data = {
	ID: string,
	Size: UDim2,
	Position: UDim2,
	AnchorPoint: Vector2,
	Animated: boolean,
	Instance: GuiObject,
	Style: Style.Object,
	
	_destroyed: boolean
}

export type Params = {
	Size: UDim2?,
	Position: UDim2?,
	AnchorPoint: Vector2?,
	Animated: boolean?,
	Style: Style.Object?
}

local module = {}
module.__index = module

type PrivateObject = typeof(setmetatable({} :: Data , module))

type ProxyData = {
	__call: (() -> PrivateObject),
	__index: PrivateObject,
	__newindex: (_: any, key: string, value: any?) -> nil
}

export type Object = typeof(setmetatable({}, {} :: ProxyData))

function module:New(instance: GuiObject, parent: GuiObject?, params: Params?): Object
	local object = setmetatable({
		ID = HttpService:GenerateGUID(false),
		Size = UDim2.new(1, 0, 1, 0),
		Position = UDim2.new(0, 0, 0, 0),
		AnchorPoint = Vector2.new(0, 0),
		Animated = true,
		Instance = instance,
		Style = BaseStyle,
		
		_destroyed = false
	}, module) :: PrivateObject
	
	for key, value in pairs (params or {}) do
		object[key] = value
	end
	
	local proxy = setmetatable({}, {
		__call = function()
			return object
		end,
		__index = object,
		__newindex = function(_, key: string, value: any?)
			if key == "Size" then
				instance.Size = value :: UDim2
			elseif key == "Position" then
				instance.Position = value :: UDim2
			elseif key == "AnchorPoint" then
				instance.AnchorPoint = value :: Vector2
			elseif key == "Instance" and object.Instance ~= nil then
				error("Cannot set Instance")
			elseif key == "ID" and object.ID ~= nil then
				error("Cannot set ID")
			end

			object[key] = value
		end
	})
	
	return proxy
end

function module.Destroy(self: Object)
	if self._destroyed then return end
	
	if self.Instance.Parent then
		self.Instance:Destroy()
		return
	end
	
	--self()._destroyed = true
	self._destroyed = true
end

return module
1 Like

It seems I fixed it, the solution for anyone who cares is to swap out the creation of the proxy to this

local proxy = newproxy(true)
	local meta = getmetatable(proxy)
	meta.__call = function()
		return object
	end
	meta.__index = object
	meta.__newindex = function(_, key: string, value: any?)
		if key == "Size" then
			instance.Size = value :: UDim2
		elseif key == "Position" then
			instance.Position = value :: UDim2
		elseif key == "AnchorPoint" then
			instance.AnchorPoint = value :: Vector2
		elseif key == "Instance" and object.Instance ~= nil then
			error("Cannot set Instance")
		elseif key == "ID" and object.ID ~= nil then
			error("Cannot set ID")
		end

		object[key] = value
	end
1 Like

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