How to make self.Property read-only


Welcome coders!

I have found a method to make properties of self(the table that returns from module.new() in OOP) read-only.
This method is not 100% convenient for everyone as it uses a function instead of directly accessing the property through self.Property, but it does work 100% of the time.


I’ll be making an OOP class called “Person” and using it as an example to show you what I mean, through code. Feel free to copy any of the code below.

Here is my setup for the OOP class “Person” written in a ModuleScript. Feel free to set it up any other way you would like:

-- ModuleScript
local Person = {}

function Person.new(firstName: string, lastName: string)
	local self = {}
	self.FirstName = firstName
	self.LastName = lastName
	
	return self
end

return Person

As you might see I am not type checking anything and will not be doing it in this post as it is irrelevant.


For the ModuleScript we will also need to add a Script. Let’s name the ModuleScript “Person” and put both inside ServerScriptService. Inside the explorer it should look something like this:

  • ServerScriptService
    – Script
    – Person

Let’s write the code for the Script and print() both chosen names:

-- Script
local Person = require(script.Parent.Person)
local person = Person.new("Bob", "Billysson")

print(person.FirstName, person.LastName)

Output: Bob Billysson

None of these properties are read-only. We can still change them as demonstrated:

-- Script
local Person = require(script.Parent.Person)
local person = Person.new("Bob", "Billysson")

person.FirstName = "Hello"

print(person.FirstName, person.LastName)

Output: Hello Billysson


Let’s make both properties read-only by adding a function that returns a property of a hidden table that can not be accessed by any other code than the code inside of Person.new():

-- ModuleScript(Person)
local Person = {}

function Person.new(firstName: string, lastName: string)
	local self = {}

	local privateProperties = {
		FirstName = firstName,
		LastName = lastName,
	}
	
	function self:GetReadOnlyProperty(propertyName: string)
		return privateProperties[propertyName]
	end
	
	return self
end

return Person

Now if we try to reach FirstName or LastName in our script without the newly added function it won’t work:

-- Script
local Person = require(script.Parent.Person)
local person = Person.new("Bob", "Billysson")

person.FirstName = "Hello"

print(person.FirstName, person.LastName)

Output: Hello nil

This code still outputs “Hello” as person.FirstName has been set to “Hello” instide of the Script.
But if we use the function we will get:

local Person = require(script.Parent.Person)
local person = Person.new("Bob", "Billysson")

local firstName = person:GetReadOnlyProperty("FirstName")
local lastName = person:GetReadOnlyProperty("LastName")

print(firstName, lastName)

Output: Bob Billysson


That was all I wanted to share. If you find a better or simpler way to do this it would be much appreciated by myself and probably many others.
Positive and negative feedback is much appreciated.


Free model:

2 Likes

Weird approach, it’s possible to make a read only property without creating an entire function for that. All you need is basic knowledge of metatables and newproxy()

Basic example:

local proxy = newproxy(true)
local data = {
	coins = 5,
	Name = "player"
}

local metatable = getmetatable(proxy)
metatable.__index = function(self, key)
	return data[key]
end
metatable.__newindex = function(self, key, value)
	-- Your logic for read-only, in my case its uppercase letter
	local firstLetter = string.sub(key, 1, 1)
	if string.lower(firstLetter) ~= firstLetter then -- its uppercase | read-only
		return error('Attempt to write to a read-only value')
	else
		data[key] = value
	end
end

proxy.coins = 10
print(proxy.coins)

print(proxy.Name)
proxy.Name = "npc" -- will error
print(proxy.Name)
4 Likes