Skip to content

Type procs, functions, and TVFs as [Args, ReturnType] tuples #22

@damusix

Description

@damusix

Summary

Procs, functions, and TVFs should be typed as tuples: [Args, ReturnType] instead of just Args.

Currently, the generic type maps only capture the input parameters:

type MyProcs = {
    get_users: { department_id: number };
    refresh_cache: void;
};

// Return type must be specified at every call site
const users = await ctx.proc<User>('get_users', { department_id: 1 });

Proposed: each entry becomes a tuple [Args, ReturnType]:

type MyProcs = {
    get_users: [{ department_id: number }, User];
    refresh_cache: void;  // shorthand for [void, void]
};

// Return type is inferred when the tuple provides it
const users = await ctx.proc('get_users', { department_id: 1 }); // → User[]

// But the generic is still available for overriding or ad-hoc calls
const users = await ctx.proc<SpecialUser>('get_users', { department_id: 1 }); // → SpecialUser[]
await ctx.proc('refresh_cache'); // → void

Rules

  • SomeProc: [Args, ReturnType] — full tuple form
  • If Args is empty/unused, use void[void, ReturnType]
  • If ReturnType is empty/unused, use void[Args, void]
  • Plain void (no tuple) is shorthand for [void, void] — no args, no meaningful return
  • proc() and tvf() currently return T[] — with tuples, return becomes ReturnType[]
  • func() currently returns scalar T — with tuples, return becomes ReturnType
  • The T return type generic remains available — it defaults to the tuple's ReturnType but can be explicitly overridden at the call site

Behavior

  1. Tuple provides return type, no generic specified → infer from tuple
  2. Tuple provides return type, generic explicitly specified → generic wins (override)
  3. No tuple / plain void → falls back to unknown (same as today), user specifies T manually
  4. Ad-hoc / untyped calls → user can still call ctx.proc<MyType>(...) for procs not in the type map, or to override the mapped return type

Affected Surfaces

  • Context class generics: Context<DB, Procs, Funcs, Tvfs>
  • ctx.proc(), ctx.func(), ctx.tvf() method signatures
  • createContext<DB, Procs, Funcs, Tvfs>() factory
  • ImpersonatedScope<DB, Procs, Funcs, Tvfs> interface
  • SDK type exports

Benefit

The type map becomes the single source of truth for both input and output shapes, eliminating repetitive annotations at call sites — while preserving full flexibility for ad-hoc calls and explicit overrides.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions