Classes (.flsh)
Reusable BrightScript-only logic: extends, override, super.
A .flsh file is BrightScript-only reusable logic, entirely separate from a
.thr component — no <script>
wrapper, no template, no XML output at all, just a class compiled straight to a plain .brs function.
The file's own base name must match the declared class name.
Fields, constructor, methods
Three visibility levels — public/private/protected —
on both fields and methods. A field can be declared top-level or, more commonly, straight inside the constructor
as the "assign a parameter to a field" shorthand:
class Counter {
constructor(start: integer) {
private count: integer = start
}
public function get(): integer {
return m.count
}
public function isZero(): boolean {
return m.count == 0
}
public function describe(): string {
return str(m.count)
}
}extends / override / super
An overriding constructor's first statement must be exactly one super(...) call.
An overriding method must be marked override — an unmarked shadow of a base
member is a compile error, on the theory that it's far more likely a missing keyword than an intentional new
member.
import Counter from "./Counter.flsh"
class LabeledCounter extends Counter {
override constructor(start: integer, label: string) {
super(start)
private label: string = label
}
override public function describe(): string {
if (m.get() == 0) {
return m.label + ": none yet"
}
return m.label + ": " + str(m.get())
}
}Using a class from a .thr component
<script>
import LabeledCounter from "components/Classes/LabeledCounter.flsh"
derived milestoneLabel: string = LabeledCounter(favoriteCount, "Favorites so far").describe()
</script>Reaching global singletons from a class method
theme/router/taskManager all
work from inside a class method exactly like they do from a .thr component's
own script — no special syntax needed on the author's side.
' Inside any class method — router/theme/taskManager all work exactly like in a .thr component:
public function goHome() {
router.navigate("/home")
}Reference implementation — apps/classes-demo
Every mechanism on this page has a router-mounted, scaled
chapter in apps/classes-demo — 3 chapters
(/fields-and-methods through
/global-singletons-from-class), reachable with REWIND/
FAST-FORWARD once compiled and sideloaded. Each chapter shows a default, no-customization
example alongside a deliberately different, customized one — e.g.
FieldsAndMethodsDemo.thr's BankAccount
(top-level AND constructor-shorthand fields, all three visibility levels) contrasts a
successful withdrawal against one deliberately exceeding the private-field-backed overdraft
limit, and ExtendsOverrideSuperDemo.thr constructs the base
and derived class side by side so override's actual effect
is visible by direct comparison. This is the live, compiling reference for the whole page — see
findings/classes-demo-app.md for what each chapter covers
and findings/demo-app-conventions.md for the app-structure
convention it follows.
⚠️ Not (yet) supported
- ○
store/state/focus(...)— entirely unreachable from a class body; a class has no reactive lifecycle at all. - ○
taskManager.onAlertChanged/onResult/onRequestSent/onResponseReceived— not supported from a class body (needs a real node identity these don't have). - ○ Lint-enforced visibility —
protectedparses but compiles identically topublic; BrightScript has no real access boundary to enforce. - ○ Multiple constructors, or a field declared more than once (or as both a field and a method).
- ○ No
.flshclass-body animation form — same root cause as the reactive-lifecycle gap above: animations are tied to a template's element ids, which a class has none of.
Exact grammar: GRAMMAR.md. Full feature status: docs/features.md.