Skip to content

Infer Workflow Result Type from WorkflowRunOperationHandler#1968

Open
VegetarianOrc wants to merge 5 commits intomainfrom
amazzeo/workflow-run-operation-type
Open

Infer Workflow Result Type from WorkflowRunOperationHandler#1968
VegetarianOrc wants to merge 5 commits intomainfrom
amazzeo/workflow-run-operation-type

Conversation

@VegetarianOrc
Copy link
Contributor

What was changed

startWorkflow now returns WorkflowHandle<WorkflowResultType> instead of WorkflowHandle, so the handler's output type is inferred as the workflow's return type (e.g. string) rather than the workflow function type. A new workflowResultType brand on WorkflowHandle makes WorkflowHandle structurally distinct from WorkflowHandle, enabling TypeScript to catch type mismatches when explicit type params contradict the actual workflow.

Why?

Previously the WorkflowRunOperationHandler was inferring the return type to be the workflow instead of the workflow's output type. This required you to explicitly type the WorkflowRunOperationHandler in order to fulfill Nexus service contracts.

Checklist

  1. How was this tested:

A test with the different combinations of typing has been added to assert compilation errors and correct inferrence.

VegetarianOrc and others added 2 commits March 16, 2026 14:59
…from typed workflows

startWorkflow now returns WorkflowHandle<WorkflowResultType<T>> instead of
WorkflowHandle<T>, so the handler's output type is inferred as the workflow's
return type (e.g. string) rather than the workflow function type. A new
workflowResultType brand on WorkflowHandle makes WorkflowHandle<X> structurally
distinct from WorkflowHandle<Y>, enabling TypeScript to catch type mismatches
when explicit type params contradict the actual workflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@VegetarianOrc VegetarianOrc marked this pull request as ready for review March 17, 2026 17:30
@VegetarianOrc VegetarianOrc requested a review from a team as a code owner March 17, 2026 17:30
/**
* A Nexus Operation implementation that is backed by a Workflow run.
*
* The type parameter `O` represents the operation's output type. When the handler uses
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's skip that comment.

test('WorkflowRunOperationHandler infers correct output type from typed workflow function', async (t) => {
// When constructing WorkflowRunOperationHandler without explicit type parameters using a typed
// workflow function, the operation output type should be inferred as the workflow's return type
// (e.g. string), not the workflow function type (e.g. (input: string) => Promise<string>).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last part of this comment will be unhelpful to future readers. It puts emphasis on what will then be a former bug, actually creating confusion.

Not totally convinced of the pertinence of the first two lines, but there's not doing any harm.

Suggested change
// (e.g. string), not the workflow function type (e.g. (input: string) => Promise<string>).

Copy link
Contributor

@mjameswh mjameswh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments. Please fix before merging,.

@Evanthx
Copy link

Evanthx commented Mar 17, 2026

Should there be unit tests for these? Or are they tested in some other way?

*
* @experimental Nexus support in Temporal SDK is experimental.
*/
readonly [workflowResultType]: T;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a better structure IMHO.

Suggested change
readonly [workflowResultType]: T;
readonly [workflowResultType]: WorkflowResult<T>;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I explored this option, but it actually conflicts with explicit typing. Including that restricts T to T extends Workflow which has knock on effects that cause any explicitly provided type parameters to also extend Workflow. For example:

// Explicit type parameters should also work correctly.
  const _explicitStringOp: nexus.OperationHandler<string, string> = new temporalnexus.WorkflowRunOperationHandler<
    string,
    string
  >(async (ctx, input) => {
    return await temporalnexus.startWorkflow(ctx, echoWorkflow, {
      args: [input],
      workflowId: 'test',
    });
  });

Instead would need to be

// Explicit type parameters should also work correctly.
  const _explicitStringOp: nexus.OperationHandler<string, string> = new temporalnexus.WorkflowRunOperationHandler<
    string,
    typeof echoWorkflow
  >(async (ctx, input) => {
    return await temporalnexus.startWorkflow(ctx, echoWorkflow, {
      args: [input],
      workflowId: 'test',
    });
  });

@VegetarianOrc
Copy link
Contributor Author

@Evanthx

Should there be unit tests for these? Or are they tested in some other way?

There are some existing tests for the functionality of the WorkflowRunOperationHandler. In this PR I included some compile time tests to ensure that patterns expected to fail do and patterns expected to succeed do not produce compile time errors.

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.

4 participants