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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions