Some more suggestions @zeuxcg
I think my favorite suggestions so far though are for table type creation on the fly and is
and as
operators
local myClassObj = setmetatable({
x: number = value or 5,
y: number = 10,
}, classMeta)
that way type deduction should be easier.
It would be interesting though to see some other way of defining metatables that is more static and conventional with other languages be introduced into LuaU, as the structure of script created objects are one of Lua’s many downsides.
example:
class Object {
Name: string,
new = function(self, name: string?)
self.Name = name or ""
end,
tostring = function(self): string -- or __tostring, more akin to python structure
return self.Name
end,
Method = function(self, data) -- it should deduce self as being Object
print(self.Name, "hello world", data)
end
}
As for function templating, consider a schema like this:
Ptr.new = function<T>(value: T): Ptr<T>
return {
Value = value
}
end
-- or
Ptr.new = function<T>(value: T): Ptr<T>
return {
Value: T = value
}
end
which would allow easy type deduction
local Val1 = Ptr.new(2) -- auto deduce Ptr<number>
local Val2: Ptr<Instance?> = Ptr.new(MyPart)
Val1.Value = 10 -- allowed
local result: {} = Val1.Value -- not allowed, incorrect type matching
local exists: boolean = Val2.Value ~= nil -- valid given the type
if exists then
local object: Instance = Val2.Value -- assume is Instance, or use "Val2.Value as Instance"
if object is BasePart then
print((object as BasePart).CFrame) -- not necessary but can make it easier to deduce type and conversions as well as error if the casting is invalid
end
end
This is another reason for the huge usefulness of a as
operator at runtime. It could error for invalid type casting conversions without even changing the value. Something like nil as Instance
should throw an error, as well as provide warnings if the deduced type will never follow that typing. And you could also introduce is
as a replacement for both typeof
and IsA
:
print(typeof(myObj) == typeof(BasePart)) --clunky
print(typeof(myObj) == "BasePart") --still clunky
print(myObj is BasePart)
is
could even detect inherentence if you so choose:
if obj is Instance then
-- potentially implicit cast here if the type is Instance? or (Instance | number) or whatever
print(obj.Name) -- should work for baseparts or models or whatever, an alternative to IsA for different hierarchies
-- more type friendly versions:
print((obj as Instance).Name)
local thing = obj as Instance
-- alternative: [local thing: Instance = obj] call "as" automatically for differing types
print(thing.Name)
end
Usage in a templated function:
local function getChildren<T>(obj: Instance): Array<T>
local list: Array<T> = {} -- extension of template into function body as local type
for _, child: Instance in next, obj:GetChildren() do
if child is T then
list[#list + 1] = child as T -- !IMPORTANT!, "as" is always only a type casting, it does not convert the object to be a BasePart class statically
end
end
return list
end
local parts: Array<Part> = getChildren<Part>(MyModel)
for _, obj: BasePart in next, getChildren<BasePart>(MyModel) do
print(obj.CFrame) -- yes ik u can cut it down to only 1 loop by using "is" in here or with an iterator, but just an example
end
local mixed = getChildren<HingeConstraint | CylindricalConstraint>(MyCarJoints)
Example errors (console, with the types checked internally from deduction or manual assignment):
> =workspace as number
print(workspace as number):1: Invalid type coercion (Workspace to number)
> local obj: Instance? = nil; print(obj as Instance)
local obj: Instance? = nil; print(obj as Instance):1: Invalid type coercion (nil to Instance)
> local obj: Instance? = workspace; print(obj as Instance)
Workspace
> local obj: Instance? = nil; if obj is Instance then print(obj as Instance) end
> -- no output above
You can think of is
as “check if type coercion is legal” and as
as “perform type coercion”