Members and fields✎
Reading a field off a value whose type says what it holds — and what to use when it does not.
Pending review
A value often holds other values: a table of settings, an instance with properties. Two nodes read one out, and which you want depends on a single question — does anything know what is in there?
| Node | Reads | When |
|---|---|---|
| Get Member | A field the type declares | The type says what it holds: a Declare Type's fields, a Roblox class's properties. The list is offered, and for a type this file declares, a name not on it is refused before the file is written. |
| Get Field | Any key you name | Nothing can promise what is in there: a dictionary filled and emptied as the program runs, or whatever require returns, which arrives untyped. |
A type you declared
A type is declared by a Declare Type node — at the top of the file or where it sits — and what it is written as decides whether it has members to offer. A table of fixed fields does; everything else is a type Luau understands and this cannot enumerate.
| Written as | Example | Members |
|---|---|---|
| Table of Fields | Rows in the Inspector: throttle, number | Each row, with its type. The ordinary case, and the one the editor checks as you type it. |
| Custom Luau, a table | { throttle: number, aim: Vector3 } | The same fields, read out of the text — so a type typed out by hand behaves as the rows do. |
| Custom Luau, anything else | "idle" | "driving", (number) -> string | None. There is no fixed field list to offer, and a Get Member on one is refused. |
| A dictionary type | { [string]: number } | None: the keys are the program's business. That is Get Field's case. |
| Type of a Value | typeof(Tuning) | None here. Luau resolves it; Roswaal does not follow it, so its members are Get Field's. |
The type's name is what travels. A Declare Local, a function's parameter, a variable or a Cast set to Input gives a pin typed Input, and every pin typed Input offers the same fields — the declaration is read wherever it sits in the file, including from inside a function.
The ordinary case. Input holds a throttle, a steer and an aim, each entered as a row; the local is that type; the pill reads aim and hands on a Vector3.
export type Input = { throttle: number, steer: number, aim: Vector3, } local input: Input = { throttle = 1, steer = 0, aim = Vector3.zAxis, } print(input.aim)
The result is typed as the field is. aim gives a Vector3 pin and throttle a number one, so what comes out wires into a Vector3 node without a Cast.
A type written as Custom Luau — click its Definition to open the code editor — behaves as the rows do, as long as what you wrote is a table of named fields. This is the shape to reach for when a field's own type is more than a name — { Player }, Model?.
export type Shot = { damage: number, from: Vector3 } local shot: Shot = { damage = 25, from = Vector3.zero } print(shot.damage)
A dictionary type, a union, a function type: Luau understands each of them, and none has a field list anybody can write down. Get Member offers nothing and refuses a member on one — Get Field is the node, and the key is a pin you fill in or wire.
export type Scores = { [string]: number } local scores: Scores = { alice = 12, bob = 9 } print(scores.alice)
A type a module exports
A module's exported types are offered here too, under the name this graph writes them as: require Tank.Config as Config, and a value annotated Config.Tuning offers that type's fields. The fields are read from the graph that declares them, so renaming one there changes what is offered here.
Two files, one idea
local ReplicatedStorage = game:GetService("ReplicatedStorage") local Config = require(ReplicatedStorage.Tank.Config) local tuning: Config.Tuning = Config.tuning print(tuning.turnRate)
Both nodes appear, and which is which is the point. The module's own table is a value nothing here can describe — Roswaal does not read the other file's returns — so Config.tuning is a Get Field. What comes out is annotated as a type that is described, so turnRate off it is a Get Member.
Info
A Roblox instance
An instance's properties come from the engine, so they need no declaring. A node that names a class hands back that class — New Instance set to Part gives a Part — and a Part's properties are then what Get Member offers.
local part: Part = Instance.new("Part") if part.Anchored then end
Inherited properties are there too: Name and Parent come from Instance, and a Part offers them alongside its own. Get Service gives the service's own class, and the Find First Child and Ancestor nodes follow their Class Name, so each of those leads straight into a member without a cast first.
Warning
Finding one
| Gesture | What you get |
|---|---|
| Drag a wire out of a typed pin | That type's members in the node menu, under their own heading. Picking one places a Get Member already wired |
Type input. in either node search | Every member of everything this graph names — a variable, a local, a parameter. Picking one places the getter and the Get Member on it, wired |
| Select a Get Member | Its Member in the Inspector, as a list of what the wired type declares |
When there is no list
A table whose keys come and go while the program runs has no fixed members, and nothing offers any: that is Get Field, which takes the key as a pin and reads whatever is there. A key that is not a Luau name — a number, or a string with a space in it — is Get Field's as well, since t.my key is not something Luau can write.
Warning
Not reviewed yet