Class | An easy and smart way of extending Instances with full intellisense support

May I ask how to make the typing for the class then, its just for autocomplete purposes.

I dont think its this

export type ComponentClass<T> = typeof(setmetatable({} :: {
    __instance: T
}, ComponentClass))
1 Like

I updated the code to export the type class for you to use!

Edit: I initially did that at the start but for some reason it was giving me DataModel before instead of instance type. I probably typed something wrong the first time around but it now works as expected.

1 Like
local Class = require("./class")

local Component = {}

function Component.Get<T, K>(self: Class.Class<T, K>)
	return getmetatable(self).__instance 
end

function Component.SetProperty<T, K, U>(self: Class.Class<T, K>, property: string, value: U)
    local instance = self:Get()
    local _, success = pcall(function()
        return (instance :: any)[property] and not (instance :: any):FindFirstChild(property)
    end)

    if success then
        (instance :: any)[property] = value
    end
end

So like this?

1 Like

It pretty much looks good, yeah. I also updated the sample code in the original post (see method FullIntellisense).

Read Usage & Example.

1 Like

Oh yea I saw it, btw I’m talking about when writing class Intellisense. Btw nice job I really like your module!

Because I’m used to use like class.get(self, …) instead of class:get(…) when writing module

2 Likes

Do you know if I write it right for self type when writing a class?

Since for you, you use like class:method(…), but as you can see I write it with class.method(self, …) for intellisense, so how is it for the typing?

1 Like

I essentially have to redeclare self to have proper type checking. I also do function Class.method(self, ...) but it’s just syntatic sugar in the end.

function Class:Method(): ()
    local self: Class<Instance, typeof(Class)> = self
    -- though LSPs/linters wont like shadowing variables,
    -- so either rename the variable or do function Class.Method(self)
end
1 Like

This is really cool. Ive been wanting something like this for a while, but never knew how to do it. I didnt even know types allowed this.

Although, would this cause memory issues if used (for instance) for every player in a 30 player server?

Also, is there any reason not to have the type set like this:

local PlayerClass = {
	Loaded = false,
}

type PlayerClass = Player & typeof(PlayerClass)

As opposed to what you suggested:

local ServerStorage = game:GetService("ServerStorage")

local Class = require(ServerStorage.Class)

local PlayerClass = {
	Loaded = false,
}

type Class<instance, class> = Class.Class<instance, class>
type PlayerClass = Class<Player, typeof(PlayerClass)>

I didnt notice a difference, but Im asking just in case

2 Likes

Well as long as you remove any references it should be properly garbage collected. Otherwise, feel free to let me know or modify if you need to. This is nothing but a table class in the end.

And yeah, you can do that
(I posted an example under the method NearFullIntellisense) Read Usage & Example
but it’s more for those who also wants intellisense for getmetatable(class) or those who needs the absolute type class.

Edit: Correct me if I’m wrong, but I believe you have to order it as typeof(PlayerClass) & Player as opposed to Player & typeof(PlayerClass) or else your override methods won’t show on autofill.

2 Likes

My only concern is that if I want to use a class with properties for multiple instances, I have to clone the entire class, which is a bit unusual compared to more traditional OOP modules (which usually have a constructor function). Of course you would have to set the properties anyway, but the functions being cloned is slightly worrying. I dont think it would matter enough to be an issue, but I dunno

You are right, I forgot to test that :sweat_smile:

1 Like

That is totally fair, and it shouldn’t really be an issue. When you decide to use table.clone() or a custom clone, all you are doing to non-table values is simply this:

Read FAQ.

If you have a function, you’re not actually cloning that function, but simply pointing to that function reference. It is essentially the same behavior as your standard OOP (by creating a new table), but the difference is that there is no metatable attached to that class.

Read FAQ.

Additionally, the reason why you cannot do something like Class(player, PlayerClass.new()) is because of the fact that Class attaches a metatable to your class, so it will just completely override your other constructed metatable (or will error if you have a __metatable index).

Read FAQ.

Edit: Thank you for the questions by the way, I will probably update the original post to address all these concerns and to format it better :smiley:

Edit 2: FAQ has been updated!

2 Likes

I updated the code to allow for new properties instead of throwing an error. You will have to set the allowNewProperties argument to true to bypass the legacy behavior.

Read Usage & Example for more information.

1 Like

hey, do you know how like uhm class a base component and connect it with another component,

lets say like BaseComponent is a class. and i want to connect it with another class?

1 Like

like let’s say PlayerClass and i wanna make AdminClass and connect it so method on PlayerClass works but also added with AdminClass methods

1 Like

I only made this module with attaching one class in mind, you’re going to have to rewrite one yourself. It’s probably possible by completely rewriting the metatable functions and by removing the Classes[instance] condition, but I say probably because I don’t know if Intellisense will like that.

Maybe I’ll end up writing a multi-class version soon, no guarantees though.

1 Like

When you are able, can you give me an example of it? like a small one. Thanks!

1 Like

Hey do you plan to make it support built in method, like maybe add GetDescendantsWhichAre etc. Thanks!

I already have made public code something similar to what you are looking for, feel free to copy over:

never realised it was yours, sorry. Yeah I found this. Thanks btw! But maybe like combine it with class, inside just one module?

any way of getting this module without Wally?