Skip to content

feat: accept a definition object for a resource's type arguments - #79

Closed
moetemp wants to merge 1 commit into
review/p3-genericfrom
review/p3-definition
Closed

moetemp wants to merge 1 commit into
review/p3-genericfrom
review/p3-definition

Conversation

@moetemp

@moetemp moetemp commented Oct 28, 2026

Copy link
Copy Markdown
Owner

What changed?

This PR adds @nexus.definition, which names an SDK type that gives the type arguments of a generic resource.

/// @nexus.type-parameter "body-t"
/// @nexus.definition python="temporalio.common.StreamDefinition"
///   typescript="StreamDefinition" typescript-import="@temporalio/common"
///   dotnet="Temporalio.Common.StreamDefinition"
resource %stream { ... }

An operation that returns the resource takes the definition in place of its runtime types.

  • Python. The create takes definition: type[StreamDefinition[BodyT]] as its first positional parameter, so create_stream(Turns, stream_id=...) returns a Stream[Turn] when Turns subclasses StreamDefinition[Turn]. The support helper definition_type_arguments(definition) returns the runtime types. The module of the type is imported under typing.TYPE_CHECKING, since the SDK module that defines it can import the generated package.
  • TypeScript. The create takes definition: StreamDefinition<BodyT> before its request, and the support helper definitionTypeArguments(definition) returns the type info. A relative typescript-import is relative to the root of the generated package.
  • .NET. The public create takes StreamDefinition<TBody> definition, which gives the type argument.

nexgen refuses @nexus.definition on a resource with no @nexus.type-parameter.

The stream sample's Stream takes a definition. No SDK has StreamDefinition yet, so the sample names a type of its own support code in each language.

It is for review here before it goes upstream. It sits on the upstream stack temporalio#308, temporalio#310, temporalio#311, temporalio#297, temporalio#296, temporalio#303, temporalio#304, temporalio#309, temporalio#318, temporalio#319, temporalio#320, temporalio#321, temporalio#322, temporalio#323, temporalio#324, temporalio#325, whose head is review/p3-base, and on #77.

Why?

The Stream API of the proposal creates a Stream from a definition, as in client.create_stream(Turns, id=...). A definition has the value type and the Stream name, and its form is an SDK idiom in each language, like a Workflow definition. So the SDK defines it, and nexgen accepts it where a runtime type goes. A later PR lets a source read the definition, for the Stream id of a Workflow.

The methods of a resource keep the runtime types of the handle, so only an operation that returns the resource takes a definition. The design also had a standalone function whose output needs a runtime type take one, but the TypeScript and .NET resource methods call those functions, and they have no definition.

How did you test it?

A Python test creates a Stream from a definition class and checks Stream[Turn] with assert_type under basedpyright. A TypeScript test checks that a create keeps the type info of the definition, and its type with expectTypeOf. A .NET compile check creates a Stream<Turn> from a definition. A unit test checks that only the create takes the definition, and a rejection fixture covers the refusal.
cargo validate passes.

`@nexus.definition` names an SDK type, such as a Stream definition, that gives the type arguments
of a generic resource. An operation that returns the resource takes it in place of runtime types.
@moetemp

moetemp commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

Duplicate of #80, which has the same commit.

@moetemp moetemp closed this Oct 28, 2026
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