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 ProductId declares a kind of string id the system defines; shot check checks 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 Xthe object's type (fields and methods), plus XClass, the class table's type (see Classes)
a module Xthe module's type (properties and functions)
a func Xthe 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
end

Put 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.Walletnamed types of a system that has an entry point
import * from @shot.Walletevery public type of that system
import Prize, Odds from @shot.Prizes.Prizefrom a folder without an entry point: name any file of it
import Wallet from Walleta 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).