Skip to content

API manifest: carry argument + return types so generated .d.ts has real signatures (not (...args: any[]) => any) #512

Closed

Description

@proggeramlug

Followup to #463 / #465 / #455 DX thread.

The auto-generated .d.ts (perry types → .perry/types/stdlib/index.d.ts) and the markdown reference (docs/src/api/reference.md) currently emit:

declare module "bcrypt" {
  /** stdlib */
  export function compare(...args: any[]): any;
  /** stdlib */
  export function hash(...args: any[]): any;
}

Loose any[] => any only tells the user a symbol exists. It doesn't help editors catch wrong arg counts, wrong shapes, or expected promise vs sync. Real type checking against argument shapes still leans on @types/node, which doesn't know what's actually implemented.

This was acknowledged at #466 Phase 2 ("argument types deferred — followup") and v0.5.531 (the manifest-v1.md spec accepts params / returns but the in-tree manifest entries don't populate them yet).

What's needed

  1. Extend crates/perry-api-manifest/src/entries.rs::ApiEntry (or its kind) with params: Vec<ParamSpec> + returns: Option<TypeSpec>. The shapes already exist for external manifests in crates/perry/src/commands/compile/resolve.rs::parse_native_library_manifest (params / returns per the v1 spec at docs/src/native-libraries/manifest-v1.md) — same enum can be reused so the in-tree and external paths share one type.
  2. Backfill 397 entries × ~3 fields each. Most can be auto-derived from crates/perry-codegen/src/.../NATIVE_MODULE_TABLE's arg-coercion table (NA_STR → string, NA_F64 → number, etc.) so this is mostly a structural transform, not 397 hand-curated entries. Class-method entries + the hand-curated module entries (crypto / os / path / process) need eyeballs.
  3. Update crates/perry-api-manifest/src/emit.rs::emit_dts to emit real signatures.
  4. The drift gate in .github/workflows/test.yml::api-docs-drift keeps everything in sync once the manifest is the source of truth.

Acceptance test

Justin's repro should typecheck against the generated .d.ts:

import { hash } from 'bcrypt';
hash(123, "salt");  // should error: argument of type 'number' not assignable to 'string'

Today: passes (returns any). After: tsc errors at the bad arg.

Why this matters

The DX upgrade is real but plateaus here. Stage 1 (#463) tells you "this symbol exists." Stage 2 (this issue) tells you "you're calling it wrong." Without stage 2, users still need to read source / trial-and-error to figure out shapes — exactly the workflow Justin called out in #455.

Refs: #455, #463 (closed), #465 (closed), #466 (Phase 2), v0.5.531 (manifest-v1 spec).

Activity

  1. added a commit that references this issue on May 6, 2026
    8a04276
  2. added 2 commits that reference this issue on May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    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