Skip to content

fix(rivetkit): type c.conn.state from createConnState in actions - #5856

Draft
eersnington wants to merge 1 commit into
mainfrom
stack/fix-rivetkit-type-conn-state-from-createconnstate-urqpmypm
Draft

eersnington wants to merge 1 commit into
mainfrom
stack/fix-rivetkit-type-conn-state-from-createconnstate-urqpmypm

Conversation

@eersnington

@eersnington eersnington commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Infer c.conn.state in actions from the return type of createConnState. Before this change, it was unknown when the createConnState context parameter had no annotation.

const user = actor({
	createConnState: (_c, params: { token: string }) => ({ userId: params.token }),
	actions: {
		whoami: (c) => c.conn.state.userId, // was `unknown`, now `string`
	},
});
  • actor() in src/actor/definition.ts has two overloads:

    Overload Types c in actions through Used when
    1 actions?: TActions & Actions<...> createConnState comes before actions
    2 The TActions constraint, as before Overload 1 does not match. For example, actions comes before a hook with an annotated params or input
  • All configs that compiled before still compile.

  • tracing uses NoInfer<TActions>, so tracing.actions can come before actions and name only some of them.

  • ActorConfigInput keeps its type. The new ActorConfigInputBase holds every field except actions.

  • Trade-off: if the shape of the config object is wrong, for example an unknown key or a non-function action, TypeScript shows No overload matches this call with one error for each overload. Errors inside hooks and actions do not change.


This is part 1 of 2 in a stack:

@railway-app

railway-app Bot commented Oct 11, 2026

Copy link
Copy Markdown

This PR was not deployed automatically as @eersnington does not have access to the Railway project.

In order to get automatic PR deploys, please add @eersnington to your workspace on Railway.

@claude

claude Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Review (updated for the overload-based approach)

The PR now uses two actor() overloads instead of the earlier single signature with a conditional return type. This review replaces my previous comment, which described the old approach.

What looks good

  • The overload approach is documented well in the comment above actor(). Moving actions out of BaseActorConfig and into an intersection is what lets c.conn.state infer from createConnState.
  • Overload 2 is the previous signature, so configs that compiled before still compile.
  • NoInfer on tracing.actions and on the Actions<...> parameters stops those positions from driving inference. A wrong annotation on c therefore errors on that action.
  • tests/conn-state.types.ts covers the main cases: inferred conn state, actions before annotated hooks, tracing before actions, client-side action types, and three @ts-expect-error cases. It is wired into tsconfig.json like token-issuance.types.ts.
  • This is type-level only, so there are no runtime or security concerns.

Concerns and suggestions

  1. NoInfer needs TypeScript 5.4 or newer. Please confirm the minimum TS version RivetKit supports for downstream users. On older versions this breaks their builds.
  2. Overload error quality. A shape error now gives "No overload matches this call" with one error per overload. The @ts-expect-error tests only check that an error exists. Consider a test that an error inside an action body still points at that action.
  3. Missing test cases.
    • An actor with no createConnState: assert whether c.conn.state is unknown or undefined.
    • An annotated c in createConnState.
    • c.state inferred together with c.conn.state, with createState before actions.
    • A nested action group with one invalid leaf, to confirm the error stays localized.
  4. Extract<TActions, Actions<...>> in the overload 1 return type. For a plain object it either returns TActions or collapses to never. A collapse would silently give ActorDefinition never actions and produce confusing client-side errors. A test that fails if the extracted type is never for a valid config would guard this.
  5. Comment placement. In config.ts, the NoInfer comment sits above the JSDoc of tracing, so it reads as detached. Put it directly above the tracing?: field, after the JSDoc.
  6. Verification. Please confirm pnpm check-types passes across the workspace, including examples and the driver-test fixtures, since this touches a public type.

Overall this looks good. Items 1 and 3 are the ones I would address before merging.

@eersnington
eersnington force-pushed the stack/fix-rivetkit-type-conn-state-from-createconnstate-urqpmypm branch from da4a60c to 72ce828 Compare October 11, 2026 20:26
@eersnington
eersnington force-pushed the stack/fix-rivetkit-type-conn-state-from-createconnstate-urqpmypm branch from 72ce828 to f72321e Compare October 18, 2026 00:44
@eersnington
eersnington marked this pull request as draft October 18, 2026 03:50

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant