Skip to content

Classes, when the platform demands one

Hem’s domain code is records, functions, and variants. But Durable Objects, custom elements, and Error subclasses require class declarations, so a deliberately minimal form exists:

export class Counter extends DurableObject<Env> {
  mut count = 0
  static kind = "counter"

  fetch(req: Request): Response {
    this.count += 1
    return Response.json({ count: this.count })
  }
}

This is a bounded interop form, not Hem’s domain model. Generic class headers and an extends clause pass through to TypeScript. An omitted constructor uses JavaScript’s implicit constructor; an explicit constructor, prototype method, or static member is preserved for platform protocols that require those shapes. The override modifier is preserved on fields and methods so TypeScript can verify framework base-class contracts. Instance and static fields are readonly by default, and mut opts into a writable field. Arrow-valued fields remain available when per-instance function identity is intentional. Class-local this and super work, and Hem’s == / != still emit strict equality inside class members.

Decorators, getters/setters, declaration-only fields, abstract/declare/accessor/explicit readonly members, and other class-member forms produce HEM3003. Use the supported class subset or a separately compiled TypeScript module, subject to the same trust rules as other imports. Access modifiers, mut methods, async field modifiers, and constructor parameter properties produce HEM3007 instead. Parameter properties are excluded by the erasable-output contract that lets output run under Node type stripping; use explicit fields and constructor assignments. Write an asynchronous field value with an async arrow initializer. Member bodies retain native declarations and control flow. Hem expressions work in returns, initializers, and call statements (including arguments and pipes). Statement-producing expressions can hoist in returns/calls or before a standalone local declaration; fields, parameter defaults, loop heads, and grouped initializers require an ordinary Hem helper. Other structural Hem/native statement mixtures receive an authored HEM3003 refusal. The formatter preserves member body text. Plain let remains immutable in these regions; use let mut for writable local storage. Immutable const and native switch, loops, labels, comma expressions, function declarations, and updates on writable locations remain supported. Boolean conditions, numeric +, foreign any demotion, assertion refusal, readonly access, refinement invalidation, and must-use still apply inside nested callbacks and initializers. Write let _ = work() for an intentional discard. var and definite-assignment ! are refused. Behavioral object literals and dynamic this contracts outside a class remain TypeScript boundaries.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close