Skip to content

TypeScript codegen: forward reference in types.ts causes ReferenceError at runtime #4584

@redrockvp

Description

@redrockvp

Bug Summary

The TypeScript code generator (spacetime generate --lang typescript) for Rust modules sometimes emits direct property references to type objects in types.ts that are defined later in the file. This causes a ReferenceError: Cannot access '<Type>' before initialization at runtime.

The codegen already handles this for some types by using lazy get accessors, but it misses certain cases, producing a mix of lazy and eager references in the same object literal.

Error

types.ts:733 Uncaught ReferenceError: Cannot access 'TileOverride' before initialization
    at types.ts:733:39

Version

  • SpacetimeDB CLI: 2.0.3
  • Module language: Rust
  • Client: TypeScript (React via spacetimedb/react)

Root Cause

In the generated types.ts, type objects are defined in a specific order. When type A contains a field referencing type B, and B is defined after A, the reference must be deferred.

The codegen already uses lazy get accessors for some fields:

// Correct — lazy evaluation, works with forward references
get terrainProfile() {
    return __t.option(TerrainProfile);
},
get placementConstraints() {
    return __t.option(PlacementConstraints);
},

But for other fields in the same object, it emits eager references:

// Broken — eager evaluation, TileOverride not yet defined at this point
tileOverrides: __t.option(__t.array(TileOverride)),

The inconsistency suggests the codegen has logic to detect forward references and emit get accessors, but that logic doesn't cover all cases — possibly missing when the type is wrapped in __t.array() or __t.option(__t.array(...)).

Reproduction

  1. Create a Rust SpacetimeDB module with two types where type A has a Vec<B> field and B is defined in a separate file that sorts alphabetically after A
  2. Run spacetime generate --lang typescript --out-dir ./bindings --module-path ./server
  3. The generated types.ts will contain an eager reference to B inside A's definition
  4. Import types.ts in a client → runtime crash

Workaround

Manually edit the generated types.ts to wrap the offending line in a lazy getter:

// Before (broken):
tileOverrides: __t.option(__t.array(TileOverride)),

// After (fixed):
get tileOverrides() {
    return __t.option(__t.array(TileOverride));
},

This must be reapplied after every spacetime generate run.

Suggested Fix

The codegen should use lazy get accessors for all fields that reference other user-defined types, not just some. Alternatively, topologically sort type definitions so that referenced types are always emitted before their dependents.

Environment

  • Windows 11 Pro
  • SpacetimeDB 2.0.3

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions