Supported syntax
Available
Section titled “Available”Values and functions
Section titled “Values and functions”const ARROW = 16;const STEP = 8;const WHEEL = 45;const CAP = 5;const MIN_THUMB = 10;
/** Puts the thumb where the content's scroll position says it should be. */const trackScroll = (gutter: Ref, thumb: Ref, capTop: Ref, capBottom: Ref, content: Ref): void => { const range: Int = Math.max(1, content.scrollHeight - content.height); const travel: Int = gutter.height - thumb.height; const y: Int = ARROW + travel * content.scrollY / range;
thumb.y = y; capTop.y = y; capBottom.y = y + thumb.height - CAP;};constandlet. A local may carry a type or leave the build to infer it.- Functions, with typed parameters and a declared return type — always a
constbound to an arrow, as below. - A function returning
Componentis a component; one returningUIdeclares an interface; anything else is an ordinary function. An exported function with typed parameters is also what the server can call — see Server entry points.
A module-level const with no type annotation is a constant. With a type —
const GAP: Int = 4 — it cannot be read inside a function or a component
(TS2E003). Leave the annotation off.
Functions are const bound to an arrow
Section titled “Functions are const bound to an arrow”One form covers everything — components, helpers, handlers:
const QuestEntry = ({ name, state }: { name: String; state: QuestState }): Component => ( <text x={4} w={{ fill: 8 }} h={ROW_H} text={name} font={Fonts.PLAIN} valign="middle" shadow color={state === QuestState.COMPLETE ? Colors.GREEN : state === QuestState.IN_PROGRESS ? Colors.YELLOW : Colors.RED} options={['Read journal']} optionSubject={name} />);Concise body when it is a single expression; braces and a return when it is
not:
const rowHeight = (ware: Ware): Int => (ware.restock ?? 0) > 0 ? 38 : 26;const restoreDefaults = (): void => { const [, setRoofs] = useState(roofRemoval); const [, setShiftDrop] = useState(shiftClickDrop); const [, setOrbs] = useState(dataOrbs); const [, setLevelUps] = useState(levelUpMessages); const [, setLevel] = useState(zoom); setRoofs(1); setShiftDrop(0); setOrbs(1); setLevelUps(0); setLevel(1);};A function is declared at module level. An arrow declared inside another
function may be a handler and nothing else: onClick={reset} where reset
is a local arrow is fine; calling reset() from another handler is refused
(TS2E120). A named function is not a value either — it cannot be
passed to a function, returned from one, or stored in state.
Declarations
Section titled “Declarations”enum Category { TOOLS, CARE, SEEDS }type Ware = { item: Item; name: String; price: Int; category: Category; /** How many the stall restocks to. Absent on the one-off pieces. */ restock?: Int;};export declare const SlotIcons: typeof GameEnums.ENUM_904;enum— a set of named whole numbers. Always a constant. Anenumwhose members the server keys on is marked@wireand given written values; see Server entry points.typeandinterface— a props type for a component, or a record type for the rows of a collection. A field may be optional.declare const … : typeof …— an alias for a dictionary entry, carrying its type. See Reading game data.listOfandmapOfat module level declare a collection.
Returning several values
Section titled “Returning several values”const [tab, setTab] = useState(openTab);Tuple return with array destructuring at the call site. This is the only destructuring form, apart from props.
Control flow
Section titled “Control flow” const tick = useInterval(paused === 1 ? undefined : () => { if (left === 0) { tick.stop(); } else { setLeft(left - 1); } }, { everyMs: 1000 });const Special = ({ i }: { i: Int }): Component => { const ware = WARES.at(i); if (ware === null) { return null; } return ( <text x={0} y={0} w="fill" h={16} text={`Special: ${ware.name}, ${ware.price} gp`} font={Fonts.PLAIN} valign="middle" shadow color={Colors.YELLOW} /> );};if/else if/else.while. The one loop; a counter is written out —let i: Int = 0; while (i < ids.length) { … i = i + 1; }— and it is how an array the server hands an entry point is walked.switchon a whole number or anenum, with constant cases. Cases never fall through, so a trailingbreakis allowed and does nothing.return. A component mayreturn nullto draw nothing, and may return early under a guard; a statement after an early return of markup is refused (TS2E226).
null is a word the language uses in three places, and nowhere else:
- A component returns
nullto draw nothing, and a? :arm inside markup may benullfor the same reason. - A collection read by a computed index or key is
Value | null. A number or a string is discharged with?? fallback; a record with=== null. See A read that can miss. - A handler prop set to
nulltakes the handler off. See Events.
=== null is the one comparison against something that is not a number.
Operators
Section titled “Operators”+ - * / % | Whole-number arithmetic. / truncates |
+= -= *= /= %= ++ -- | On a local |
& | | Bit by bit, on whole numbers |
=== !== | Equality, between two numbers or against null |
< > <= >= | Comparison |
&& || | Logic, on true/false values |
! | Negation, on a Boolean only |
? : | Conditional expression |
?? | The fallback for a read that may miss |
x! | On a parameter field of an item, an NPC or a world object — item.weight! — and on a component’s geometry read through a ComponentId. Nowhere else |
`…${x}…` | Template literal. Any value interpolates |
A comparison is a value: hide={n === 0} and const big: Boolean = n > 2
both compile.
Markup
Section titled “Markup”Tags written inline, as described in Writing markup.
Markup can be bound to a const and used by name, which splices it in where the
name appears — see Composing components.
A name is not a tag, though: <banner /> for a const banner is refused by
the type checker before the build starts. Declare a component when you want
one.
Imports
Section titled “Imports”import { createRef, useInventory } from 'ts2';
import { SunkenBox, Window } from './chrome';import { Colors, Fonts, Icons, Inventories, Items } from './refs';import { FarmingItems } from './refs-describing';Only from 'ts2' and from other .ts2 files in the project. import { X as Y }
renames.
Not available
Section titled “Not available”The Instead column is the shape to write.
| Instead | |
|---|---|
for, for…of, for…in | while (TS2E013) |
break, continue | Restructure the loop’s condition (TS2E014) |
try, catch, throw | There are no exceptions (TS2E010) |
class, new | Functions, records and components. A class cannot be built (TS2E003) |
async, await, Promise | Nothing is asynchronous. See Timers |
Generators, yield | — |
==, != | ===, !== (TS2E011) |
=== between two strings | Test .length, includes, startsWith; or interpolate (TS2E003) |
typeof, instanceof | Types are known at build time |
?. | A read that may miss is Value | null — discharge it with ?? or === null (TS2E003) |
&&=, ||= | Write the assignment out (TS2E002) |
<<, >>, ^, ~, ** | *, / and Math.pow (TS2E003) |
Spread, ... | Pass values individually. A spread inside a constant array is not a constant (TS2E212); on an element, TS2E232 |
| Object literals as values | A record in a collection or a constant array, or ref.props = { … } — nowhere else (TS2E003) |
| Arrays built at run time | A collection or a constant array. Int[] has no type the client holds (TS2E100) |
| Functions as values | A Ref to the thing that should change (TS2E101) |
| A function declared inside another and called by name | Declare it at module level (TS2E120) |
| Truthiness | An explicit comparison (TS2E110); ! on a number, TS2E111 |
? : in a while condition | Compute it inside the loop body (TS2E012) |
x! on a plain value | Only a parameter field or a component’s geometry may be asserted (TS2E003) |
Conditional expressions in a while
Section titled “Conditional expressions in a while”? :, a collection read and ?? are refused in a while condition. Compute
them inside the loop body.