livestorejs/livestore

Type Annotations on schemas not portable

Open

#383 opened on Jun 18, 2025

View on GitHub
 (2 comments) (0 reactions) (0 assignees)TypeScript (128 forks)github user discovery
help wantedneeds-repro

Repository metrics

Stars
 (3,599 stars)
PR merge metrics
 (PR metrics pending)

Description

When publishing LiveStore schemas to registries (JSR, npm), the publish process encounters type annotation issues.

xref: #307 - publishing the core LiveStore packages on jsr.

Context: I'm building a collaborative notebook system using LiveStore and need to publish the schema so I can create a Deno-based execution worker that shares the same schema definitions as the main Node.js application. This pattern is useful for cross-runtime consumption and external tooling integration.

When attempting to publish, you'll see the following error:

error[missing-explicit-type]: missing explicit type in the public API
export const tables = {
             ^^^^^^ this symbol is missing an explicit type

This occurs for tables, events, materializers, state, and schema exports.

This is not just JSR though as it affects npm publishing too. Even when creating a separate pnpm workspace package for npm publishing, the same explicit type annotation challenges arise:

// packages/anode-schema/src/index.ts
export const tables = { /* ... */ }  // TS complains about implicit types
export const schema = makeSchema({ events, state }); // Complex inferred type

npm Package Scenarios Where This Matters:

  • Publishing schema as standalone npm package for external consumption
  • Separate packages within a monorepo that need to consume the schema
  • Library authors wanting to distribute LiveStore schemas as reusable packages

TypeScript Declaration Generation:

When tsc generates .d.ts files for npm packages, complex inferred types from LiveStore's API can result in massive declaration files, type refeences that don't resolve properly, or poor IDE performance when importing the schema.

Workarounds

  1. Keep schema in shared directory (works for monorepos only)
  2. Use // @ts-ignore or type assertions (loses type safety)
  3. Use --allow-slow-types for JSR (works but with performance warnings)
  4. Manually write explicit type annotations (complex with LiveStore's API)

Cross-Runtime Package Consumption (What Works Well):

pnpm/Node.js Experience:

pnpm add jsr:@anode/draft-schema
import { schema, tables, events } from "@anode/draft-schema";
// Full type inference preserved, works identically to direct imports

Deno Experience:

import { schema, tables, events } from "jsr:@anode/draft-schema";
// Same API, but benefits from JSR's native Deno support

Conclusion

Based on the LiveStore docs, schemas are meant to be shared across packages/runtypes. The direct TypeScript import approach works great within a monorepo, but cross-runtime sharing (Node.js <> Deno) requires a registry. JSR is ideal for this since it supports both runtimes natively.

  • ✅ Functionality: Schema works perfectly in both environments once published
  • ✅ Type Safety: Full LiveStore type inference preserved for consumers
  • ⚠️ Publishing: Requires --allow-slow-types flag for JSR
  • ⚠️ Performance: JSR warns about potential type checking slowness
  • ⚠️ npm: Complex .d.ts generation for TypeScript declaration files

LiveStore's type inference creates incredibly ergonomic APIs for direct use. However, it makes types difficult to serialize for external consumption. I'm more than happy to test JSR LiveStore packages with my schema publishing scenario, especially in service to helping with #307.

Environment:

  • LiveStore: 0.3.1
  • Deno: 2.3.3
  • Node.js: 23.0.0
  • TypeScript: 5.8.3

Contributor guide