Question about Object Oriented Programming and inheritance

I have been playing around with OOP and inheritance, and as I understand it, these are the basics of it:

ModuleScript, named Block
local Block = {}
Block.__index = Block

function Block.new(Name : string) : Block
	local self = setmetatable({}, Block)
	self.Name = Name
	
	return self
end

function Block:SetName(Name : string)
	self.Name = Name
end

export type Block = typeof(setmetatable({}, Block)) & {
	Name : string
}

return Block
ModuleScript, named Door
local Block = require(script.Parent.Block)

local Door = {}
Door.__index = Door
setmetatable(Door, Block)

function Door.new(Name : string, Open : boolean) : Door
	local self = setmetatable(Block.new(Name), Door)
	self.IsOpen = Open
	
	return self
end

function Door:Open()
	self.IsOpen = true
end

function Door:Close()
	self.IsOpen = false
end

export type Door = typeof(setmetatable({}, Door)) & typeof(setmetatable({}, Block)) & {
	IsOpen : boolean
}

return Door
Script for testing
local Door = require(script.Door)

local DoorObject = Door.new("TestDoor", false)

print(DoorObject.Name) --> TestDoor
DoorObject:SetName("Test123Door")
print(DoorObject.Name) --> Test123Door

print(DoorObject.IsOpen) --> false
DoorObject:Open()
print(DoorObject.IsOpen) --> true

It works pretty well and prints what you’d expect. My only concern about it is how the autofill looks while writing code for it:
image
It includes __index, as well as all the functions, which are only supposed to be usable with the object that’s returned from Door.new().

image
Likewise, the object has the new function, as well as __index visible.

Even though none of this affects what the code does, I find it really annoying. So I started experimenting, and found this removed most parts of the problem:

ModuleScript, Block
local Block = {}
local BlockFunctions = {}
Block.Metatable = {
	__index = BlockFunctions
}

function Block.new(Name : string) : Block
	local self = setmetatable({}, Block.Metatable)
	self.Name = Name
	
	return self
end

function BlockFunctions:SetName(Name : string)
	self.Name = Name
end

export type Block = typeof(setmetatable({}, Block.Metatable)) & {
	Name : string
}

return Block
ModuleScript, Door
local Block = require(script.Parent.Block)

local Door = {}
local DoorFunctions = {}
Door.Metatable = {
	__index = DoorFunctions
}
setmetatable(DoorFunctions, Block.Metatable)

function Door.new(Name : string, Open : boolean) : Door
	local self = setmetatable(Block.new(Name), Door.Metatable)
	self.IsOpen = Open
	
	return self
end

function DoorFunctions:Open()
	self.IsOpen = true
end

function DoorFunctions:Close()
	self.IsOpen = false
end

export type Door = typeof(setmetatable({}, Door.Metatable)) & typeof(setmetatable({}, Block.Metatable)) & {
	IsOpen : boolean
}

return Door

This is the result:
image
It reduces it down to only the Metatable field.

image
image
It looks a lot cleaner, and it’s much more clear what functions and properties are actually available.

Finally, my question is: Is this a viable way of doing this, or is there another solution to this problem? Will this negatively affect any aspects of OOP, or is it fine to use this method?

1 Like

It doesn’t necesarily matter since the type checker doesn’t do anything except inform the editor autocomplete and highlighting. Lua also doesn’t have access modifiers, so it’s still useful to do this, since it helps you not make mistakes. Your second example is the more common way of setting up a metatable, though.