Skip to content

Feature: Export TypeScript Declarations (.d.ts) for All Runtime-Built Classes #10

Description

@ichiriac

Summary

Provide first-class TypeScript support by exporting declaration files (.d.ts) for all public classes and APIs (e.g., Repository.d.ts, Field.d.ts, etc.). Because classes and repositories are composed at runtime, introduce a lightweight metadata/registry and a build-time declaration generator that emits .d.ts definitions per file and a bundled types/index.d.ts entry point. This gives consumers accurate autocomplete, type inference, and safety without rewriting the runtime.

Goals

  • Export stable .d.ts for all public classes/types (e.g., Repository, Field, Query, etc.).
  • Maintain definitions per source file (e.g., src/repository.jstypes/Repository.d.ts).
  • Support the dynamic/runtime composition model while preserving accurate types in editors.
  • Publish "types": "types/index.d.ts" for out-of-the-box DX.

Non-Goals

  • Rewriting runtime to TypeScript.
  • Changing public runtime APIs.

Approach

1) Authoritative Type Surface via JSDoc or Handwritten d.ts

  • For stable, non-dynamic surfaces (e.g., core class method signatures), add JSDoc types in the source or hand-author slim .d.ts shims. This lets tsc --emitDeclarationOnly or a bundler (e.g., dts-bundle-generator) produce consistent declarations.
  • Example: types/Repository.d.ts declares constructor and methods that exist at runtime.

2) Dynamic Surfaces via Metadata + Codegen

  • Introduce a small runtime-agnostic “descriptor” layer that mirrors how classes/repositories are composed at runtime. Example descriptor:
    interface RepoDescriptor {
      name: string;
      fields: Record<string, "string" | "number" | "boolean" | "date" | "json">;
    }
  • During the build, a generator script traverses these descriptors (which can be authored, derived, or assembled from runtime composition inputs) and emits strongly-typed .d.ts interfaces and module augmentations. This preserves autocomplete for per-repo fields and other dynamic additions.
  • The generator writes:
    • Per-file declarations (e.g., types/Repository.d.ts, types/Fields.d.ts).
    • A consolidated types/index.d.ts with re-exports for "types" entry.

3) Packaging

  • package.json:
    • "types": "types/index.d.ts"
    • Include "types" directory in files array.
    • Add a build step (e.g., npm run build:types) that runs the generator before publish.

4) Maintenance Strategy

  • Prefer “definition-per-file” so ownership maps to the runtime source files.
  • Keep dynamic surfaces strictly driven by descriptors to avoid drift.
  • Add CI validation to ensure generator is run and outputs are committed or packaged.

Acceptance Criteria

  • Consumers get autocomplete for repository instances and their fields (e.g., repo.get("username") infers string).
  • types/ contains one .d.ts per public module plus index.d.ts.
  • package.json exposes "types": "types/index.d.ts".
  • Demo project compiles and shows autocomplete without needing the source code.

Alternatives Considered

  • Full TS rewrite: too invasive for current goals.
  • Pure JSDoc without codegen: insufficient for highly dynamic composition.

Demo

A minimal demo is included to show how a consumer receives autocomplete from exported .d.ts files, including dynamically-described repository fields.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions