When typeof appears in type position, it means something a little bit different than the typeof() function that you see in regular Lua.
“Type position” means a position in your code where the parser is expecting to see a type and not a value. Type annotations and after the = in a type definition, in other words.
In type position, typeof is a way to say “whatever type this expression produces.” Some examples:
--!strict
local a = 55 -- Luau infers that a: number
local b: typeof(a) = 22 -- b has whatever type a has. Number, in this case.
local c = Player.CFrame -- c has type CFrame
local v = Vector3.new(0, 0, 0) -- v has type Vector3
local d: typeof(c * v) = c -- d has whatever type c * v would have, if you
-- were to run it. c * v is not actually
-- executed!
Okay I see, thank you. Though I have a few more questions.
Why does local x: typeof({ }) = { } work, but local x: table = { } doesn’t? Why aren’t tables supported? Will more datatypes like function, thread and userdata (excluding roblox ones like Instance, Vector3, etc) be supported in the future?
Is there any way to express inheritance at the moment? The most obvious use for something like this is having a piece of code that accepts all BaseParts, not just ones that have been explicitly whitelisted in a union (that and Part|WedgePart|CornerWedgePart|MeshPart|Union and so forth is really not intuitive or fun to write).
Being able to instead write something like inherits<BasePart> (but with better syntax obviously) would be nice.
Additionally, I don’t think I ever got an answer on including an Array type by default. It’s not a very big deal but arrays are very common so it should probably be built in.
local MyPart = {}
function MyPart.new()
local NewMyPart = {} --Your new Part
return setmetatable(NewClass, BasePart) --If i am following the tutorial i linked you, so you can inherit from BasePart class. But i am unsure if Roblox will leave you do this…
end
return MyPart
While I appreciate the lesson, I’m actually referring to the Roblox class inheritance that already exists. There isn’t any documentation for this on the developer hub but the various classes all inherit from other classes on Roblox.
For example, Part inherits from a class called BasePart. You can use Instance:IsA in code to check inheritance like this, which is helpful since abstract classes like BasePart cover a lot of ground. Like I said above, it includes all of the part classes, meshparts, and unions.
I’m specifically asking for a way to specify this inheritance. There’s an obvious benefit to allowing all Parts (whether they be wedges, blocks, or meshes) to be specified as a type easily.
local t:BasePart = Instance.new"Part"
-- or with generics
type inherits<T> = T
local t2:inherits<BasePart> = Instance.new"Part"
It doesn’t check when IsA is used, so this gives a warning.
local t:BasePart = Instance.new"Part"
if t:IsA"Part" then
print(t.Shape)
end
Tables and functions aren’t represented with function and table, instead they have their own syntax
type f = () => ()
-- takes no arguments, returns no values
type f2 = (string) => boolean
-- takes a string argument, returns a boolean
type f3 = (string,number?) => (number,number?)
-- takes a string and optional number, returns a number and an optional number
type t = {[number]:string}
-- keys are numbers, values are strings
type t2<T> = {[number]:T}
-- keys are numbers, values are the specified generic type
type t3 = {a:number,b:string,c:t3?}
-- key 'a' is a number, key 'b' is a string, key 'c' is a t3 or nil
Hopefully we get thread and userdata, although userdata would only be for userdata created with newproxy.
It seems like compound if statements have some issues:
local part0: BasePart? = workspace:FindFirstChild("Part0")
local part1: BasePart? = workspace:FindFirstChild("Part1")
if part0 and part1 then
-- This gives the following warning in Script Analysis:
-- Non-table type BasePart | nil does not have key "Name"
print(part0.Name)
print(part1.Name)
end
if part0 then
if part1 then
-- This gives no warnings
print(part0.Name)
print(part1.Name)
end
end
Also, explicit nil checking like this gives the same warning as the above:
if part0 ~= nil then
print(part0.Name)
end
Side note, it would be really cool if type annotating as BasePart / Model / some other type of instance would pass onto FindFirstChild and have it only return that type. Or maybe there needs to be stricter checks for FindFirstChild/similar methods and types. Right now declaring something as BasePart like I did above but having FindFirstChild still able to return any type of Instance (or none) without warning me seems bad.
Using SetAsync on a DataStore and passing a table with a string and two tables (that have three numbers in them) seems to not work. Worked in nonstrict mode, but not strict.
type is a keyword for defining custom types that aren’t part of Luau normally. Think of it as aliasing a word to a type. It’s not a function. The {x: number, y: number} is defining what the type is. Later on, you can use the custom type like any other type:
function point_thing(point: Point) => boolean
return point.x == point.y
end
Just makes it easier than putting {x: number, y: number} every time.
And I have realized that you posted in January. Oops.
Not sure if this was mentioned already, but functions such as FindFirstChild get warnings on them even when their ‘third’ argument (recursive?) is optional.
Also, why does Luau annotations have to be the only way to mark types? Isn’t comment based deduction better and cleaner?
I honestly find these much easier to read.
This is JSDocs for JavaScript / Node.js that is used in IDEs like Visual Studio and it prevents code bloat by using comments, while also being able to provide detailed and precise descriptions of variables, parameters, functions, and etc as you type them out which is very very useful and not currently possible when all you can do is annotate a type.
FindFirstChild Takes 3 arguments.
When you call a findfirstchild(), you use a syntax suger “:”. When this is used, lua automatically takes the object “:” was used as a first argument, which can be referred by using the “self” global.
local obj = {}
obj.__index = obj
function obj.new(name: string) => object
local newObj = setmetatable({}, obj)
newObj.Name = name
return newObj
end
function obj:printName()
print(self.Name) --self refers to newObject created in script after this module script
end
type object = typeof(obj.new)
return obj
--Script
local objModule = require(obj)
local newObject = objModule.new("Bob")
newObject:printName() --By using ":", we tell the function that self refers to newObject
>>Bob
In same manner, the FindFirstChild(name, recursive)
takes 3 arguments because it uses “:” to the object you are searching in
This is correct. You need to cast your return type to Instance first because it can either be an Instance, which has the properties/methods you desire, or nil, which has no properties or methods. Your function can return an Instance, but it can also return nil since the return type is optional, hence Instance | nil. The code you provided does not cast to Instance, so the IDE gives the warning.
The other two things though are funky behavior, I presume Luau doesn’t check if an argument is optional yet.