Generated service not showing up as type `ServiceType`
BrennerSpear opened this issue · 3 comments
Describe the bug
Hello all - I am folowing the Connect docs to implement a gRPC client to hit another service I have, and it looks like I am following it to the T, but getting type errors when i import my service.
BackfillOrchestrator
, which is a service generated via buf generate
is giving me a type error in the index.ts
file
Any idea on what's going on?
# buf.gen.yaml
version: v1
plugins:
- plugin: es
out: ./packages/shadow-protos
opt:
- target=ts
- plugin: connect-es
out: ./packages/shadow-protos
opt:
- target=ts
// shadow-protos/services/backfill-orchestrator/v1/service_connect.ts
/**
* @generated from service xyz.shadow.services.backfill_orchestrator.v1.BackfillOrchestrator
*/
export const BackfillOrchestrator = {
typeName: "xyz.shadow.services.backfill_orchestrator.v1.BackfillOrchestrator",
methods: {
/**
* @generated from rpc xyz.shadow.services.backfill_orchestrator.v1.BackfillOrchestrator.CreateBackfillJob
*/
createBackfillJob: {
name: "CreateBackfillJob",
I: CreateBackfillJobRequest,
O: CreateBackfillJobResponse,
kind: MethodKind.Unary,
},
...
}
// index.ts
import { TriggerAllHourlyExportWorkflowExecutionsResponse } from "@repo/shadow-protos/services/backfill-orchestrator/v1/service_pb";
import { BackfillOrchestrator } from "@repo/shadow-protos/services/backfill-orchestrator/v1/service_connect";
import { createConnectTransport } from "@connectrpc/connect-web";
import { createPromiseClient } from "@connectrpc/connect";
export async function triggerAllHourlyBackfills(): Promise<TriggerAllHourlyExportWorkflowExecutionsResponse> {
const transport = createConnectTransport({
baseUrl: "https://localhost:50052",
});
const client = createPromiseClient(BackfillOrchestrator, transport);
// let res = await client.triggerAllHourlyExportWorkflowExecutions({});
}
The error from BackfillOrchestrator
is:
Argument of type '{ readonly typeName: "xyz.shadow.services.backfill_orchestrator.v1.BackfillOrchestrator"; readonly methods: { readonly createBackfillJob: { readonly name: "CreateBackfillJob"; readonly I: typeof CreateBackfillJobRequest; readonly O: typeof CreateBackfillJobResponse; readonly kind: any; }; ... 6 more ...; readonly tr...' is not assignable to parameter of type 'ServiceType'.
Types of property 'methods' are incompatible.
Type '{ readonly createBackfillJob: { readonly name: "CreateBackfillJob"; readonly I: typeof CreateBackfillJobRequest; readonly O: typeof CreateBackfillJobResponse; readonly kind: any; }; ... 6 more ...; readonly triggerAllHourlyExportWorkflowExecutions: { ...; }; }' is not assignable to type '{ [localName: string]: MethodInfo<AnyMessage, AnyMessage>; }'.
Property 'createBackfillJob' is incompatible with index signature.
Type '{ readonly name: "CreateBackfillJob"; readonly I: typeof CreateBackfillJobRequest; readonly O: typeof CreateBackfillJobResponse; readonly kind: any; }' is not assignable to type 'MethodInfo<AnyMessage, AnyMessage>'.
Type '{ readonly name: "CreateBackfillJob"; readonly I: typeof CreateBackfillJobRequest; readonly O: typeof CreateBackfillJobResponse; readonly kind: any; }' is not assignable to type 'MethodInfoBiDiStreaming<AnyMessage, AnyMessage>'.
The types returned by 'I.fromBinary(...)' are incompatible between these types.
Type 'CreateBackfillJobRequest' is missing the following properties from type 'AnyMessage': equals, clone, fromBinary, fromJson, and 6 more.
generated CreateBackfillJobRequest
via buf generate
/**
* @generated from message xyz.shadow.services.backfill_orchestrator.v1.CreateBackfillJobRequest
*/
export class CreateBackfillJobRequest extends Message<CreateBackfillJobRequest> {
/**
* @generated from field: xyz.shadow.libs.common.v1.types.Chain chain = 1;
*/
chain?: Chain;
/**
* @generated from field: xyz.shadow.libs.common.v1.types.Fork fork = 2;
*/
fork?: Fork;
/**
* @generated from field: xyz.shadow.libs.common.v1.types.BlockRange block_range = 3;
*/
blockRange?: BlockRange;
constructor(data?: PartialMessage<CreateBackfillJobRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "xyz.shadow.services.backfill_orchestrator.v1.CreateBackfillJobRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "chain", kind: "message", T: Chain },
{ no: 2, name: "fork", kind: "message", T: Fork },
{ no: 3, name: "block_range", kind: "message", T: BlockRange },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CreateBackfillJobRequest {
return new CreateBackfillJobRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CreateBackfillJobRequest {
return new CreateBackfillJobRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CreateBackfillJobRequest {
return new CreateBackfillJobRequest().fromJsonString(jsonString, options);
}
static equals(a: CreateBackfillJobRequest | PlainMessage<CreateBackfillJobRequest> | undefined, b: CreateBackfillJobRequest | PlainMessage<CreateBackfillJobRequest> | undefined): boolean {
return proto3.util.equals(CreateBackfillJobRequest, a, b);
}
}
protofiles
syntax = "proto3";
package xyz.shadow.services.backfill_orchestrator.v1;
import "libs/common/v1/types.proto";
service BackfillOrchestrator {
rpc CreateBackfillJob(CreateBackfillJobRequest)
returns (CreateBackfillJobResponse);
}
message CreateBackfillJobRequest {
.xyz.shadow.libs.common.v1.types.Chain chain = 1;
.xyz.shadow.libs.common.v1.types.Fork fork = 2;
.xyz.shadow.libs.common.v1.types.BlockRange block_range = 3;
}
message CreateBackfillJobResponse {
string job_id = 1;
.xyz.shadow.libs.common.v1.types.Fork fork = 2;
.xyz.shadow.libs.common.v1.types.BlockRange block_range = 3;
uint64 task_count = 4;
uint64 blocks_per_task = 5;
uint64 job_completion_estimate_in_minutes = 6;
}
syntax = "proto3";
package xyz.shadow.libs.common.v1.types;
message Chain {
// Required.
ChainId chain = 1;
// Required.
NetworkId network = 2;
}
enum ChainId {
UnknownChain = 0;
Ethereum = 1;
Optimism = 2;
Base = 3;
}
enum NetworkId {
UnknownNetwork = 0;
Mainnet = 1;
Goerli = 2;
}
message Fork {
// Required.
string id = 1;
// Optional. Default version is 0.
uint64 version = 2;
}
// Right half-open interval denoting a list of blocks.
message BlockRange {
// Start of the range (inclusive).
uint64 start = 1;
// End of the range (exclusive).
uint64 end = 2;
}
Environment:
- Node.js version:
18.19.0
{
"dependencies": {
"@bufbuild/protobuf": "^1.8.0",
"@connectrpc/connect": "^1.4.0",
"@connectrpc/connect-web": "^1.4.0",
"@google-cloud/storage": "^7.9.0",
"@google-cloud/workflows": "^3.2.0",
"@repo/shadow-protos": "workspace:*"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/eslint": "^8.56.5",
"eslint": "^8.57.0",
"typescript": "^5.3.3"
}
}
// tsconfig
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"incremental": false,
"isolatedModules": true,
"lib": ["es2022", "DOM", "DOM.Iterable"],
"module": "NodeNext",
"moduleDetection": "force",
"moduleResolution": "NodeNext",
"noUncheckedIndexedAccess": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ES2022"
}
}
Hey Brenner, I copied your TypeScript config into our example and don't see an issue.
The cause of the error message you see is:
Type 'CreateBackfillJobRequest' is missing the following properties from type 'AnyMessage': equals, clone, fromBinary, fromJson, and 6 more.
This indicates that the base class Message
from @bufbuild/protobuf
could not be resolved when the compiler tries to compile the generated code.
You can remove the comment @ts-nocheck
from the generated code to see an error message explaining why. (We generate the annotation for better BC, but it can be disable, see the documentation.)
I'm going to close this, but please feel free to re-open with a reproducible example.
My fix was adding - import_extension=.ts
I'm using turbopack
for my bundler
version: v1
plugins:
# This will invoke protoc-gen-es
- plugin: es
out: ./packages/shadow-protos
opt:
- target=ts
- import_extension=.ts
# This will invoke protoc-gen-connect-es
- plugin: connect-es
out: ./packages/shadow-protos
opt:
- target=ts
- import_extension=.ts
Thanks for posting the resolution, Brenner!
If you are using next.js, this is a known issue, see vercel/next.js#59744. If you are not using next.js, the same issue likely applies to turbopack.
The ecosystem is in a bad state right now: You have to use the .js
extension in imports for TypeScript's modern ESM resolution logic, but bundlers are slow to catch up.
The option import_extension=.ts
or import_extension=none
should both work in this case.