Types
Write a folder's types once in its Types.shot and every file of the folder can use them by name, with imports from other systems in the same place.
Each folder has one Types.shot with its imports and type definitions. Every other file of the folder points at it with a types line. Then it can name those types, and every class, module and func of the folder, without importing anything.
The Types file
Wallet/Types.shot
import Signal from @shot.Utils.Signal
import * from @shot.Market
doc { Why a player's coins changed }
public type Reason = "Pickup" | "Shop" | "Gift"
public type Entry = { Amount : number, Reason : Reason }
internal type Slot = Entry | false
public type Pair<T> = { First : T, Second : T }- One Types.shot per folder. It holds only imports and type definitions (and docs above them), and the system's event lines (
event CoinsGiven(player : Player, amount : number); see Events). - Every other file of the folder points at it with
types script.Parent.Types. A file without that line is "needs a types line (types script.Parent.Types)". public type: may leave the system. The entry point re-exports it and other systems may import it.internal type: stays inside the folder.- Order inside the file doesn't matter: a type may use one defined below it.
id ProductIddeclares a kind of string id the system defines;shot checkchecks its literals (see Id kinds).
What every file of the folder sees, without imports
- every type in the folder's Types.shot, public and internal;
- every type imported there;
- the name of every class, module and func in the folder, as a type:
a class X | the object's type (fields and methods), plus XClass, the class table's type (see Classes) |
a module X | the module's type (properties and functions) |
a func X | the function's type (also a diffsource func) |
a split module | { Client : ..., Server : ... } |
So Bank.shot can write Wallets : {[Player] : Wallet} when Wallet.shot is in the same folder, without any import.
Types in signatures come from the Types file
Field types, parameter types and return types of members are copied into the folder's Types module, so they can only use the names above. A type written in a class or module file works only in that file's own code:
type Pos = { X : number, Y : number } -- in Wallet.shot
public Move(to : Pos) -- Unknown type 'Pos'
local p : Pos = to -- fine inside a body
endPut types that appear in a signature in Types.shot.
Importing from another system
A type from another folder, a util's class too, can appear in this folder's signatures only once this folder's Types.shot imports it. Until then shot check says "TypeError: Unknown type 'Signal'; it is declared in Utils/Signal: add import Signal from @shot.Utils.Signal to Market/Types.shot".
Imports go in Types.shot, nowhere else. An import line in any other file is an error ("import only works in a Types file: move it to this folder's Types.shot").
import Wallet, Reason from @shot.Wallet | named types of a system that has an entry point |
import * from @shot.Wallet | every public type of that system |
import Prize, Odds from @shot.Prizes.Prize | from a folder without an entry point: name any file of it |
import Wallet from Wallet | a bare name: the nearest Wallet, found the way use finds it (Files, USE) |
- The path points at a system's entry point (
@shot.Wallet, for Wallet/Wallet.shot) or, when the folder has none, at one of its files (@shot.Prizes.Prize). Either way you get the public types of that whole system. import *skips names this Types file already defines or imports.- Imported types are internal here: every file of the folder can use them, but this system doesn't export them again.
- The Types file must import every outside type the folder uses in a signature, in its own type definitions, or in
extends(see Classes, INHERITANCE). A body that only uses a value (calls a function) needs a require, not an import. - An import gives a type, not a value. To call another system, also require it in the file that calls:
Wallet/Types.shot: import Signal from @shot.Utils.Signal
Wallet/Wallet.shot: local Signal = require(@shot.Utils.Signal)
...
public Changed : Signal
...
self.Changed = Signal.new()Errors:
import: nothing at @shot.Prizs: the path points at nothing, or at a folder without an entry point.import: no Walet in this folder or any folder above it (...): a bare name that isn't there.import: @shot.Wallet has no public type Reasn: no such public type there (yet).import: Slot is internal to @shot.Wallet (class types and internal types stay in their folder): the type is there, but it doesn't leave its folder.
A type has to exist before it can be used
Importing a type the other system hasn't written yet fails ("has no public type"), and a name nobody declared is "Unknown type". When you write several systems at once, write each type first in the system that owns it (a stub is enough), then import it in the others.
Scripts
Scripts have no types line. They reach types through a required entry point: Wallet.Wallet, Wallet.Reason (see Scripts).
