ts-proto: Incompatible Wrapper Types with Nest.js (protobufjs)

When using Google’s Well-Known Types with Nest (or just plain protobufjs) the types generated from ts-proto fail to be parsed.

Given this protobuf

syntax = "proto2"
import "google/protobuf/wrappers.proto";

message MonitorHelloRequest {
    optional google.protobuf.StringValue name_prefix = 2;
    optional google.protobuf.Int64Value limit = 3;
}

we get a typescript interface of


export interface MonitorHelloRequest {
  namePrefix: string | undefined;
  limit: number | undefined;
}

which seems fine. But when attempting to use that as a message that is parsed by a server using protobufjs (nest in this case)

You get the error

TypeError: MonitorHelloRequest.namePrefix: object expected

This is because protobufjs is actually expecting that interface to be


export interface MonitorHelloRequest {
  namePrefix: { value: string } | undefined
  limit: { value: number } | undefined;
}

it is unclear to me who is right here as I can see the argument for either. This was referenced on https://github.com/protobufjs/protobuf.js/issues/1042 and several other issues but there does not seem to be any update in a very long time from protobufjs on the matter

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 5
  • Comments: 21 (12 by maintainers)

Commits related to this issue

Most upvoted comments

Are there any workaround for this issue yet?

I think i finally found a workaround for this problem after spending the whole day on that:D We can update the wrapper object from protobufjs like the following:

import { wrappers } from 'protobufjs';
wrappers['.google.protobuf.Timestamp'] = {
  fromObject: function (value) {
    return {
      seconds: value.getTime() / 1000,
      nanos: (value.getTime() % 1000) * 1e6,
    };
  },
  toObject: function (message: { seconds: number; nanos: number }, options) {
    return new Date(message.seconds * 1000 + message.nanos / 1e6);
  },
} as any; // <- dirty workaround :D

This can, of course, be extended to incorporate other well known protobuf messages.

To automatically run this code, I created a small package, with only one file (index.ts) looking like the following:

import {
  loadSync as _loadSync,
  Options,
  PackageDefinition,
} from '@grpc/proto-loader';
import { wrappers } from 'protobufjs';

export const loadSync = (
  filename: string | string[],
  options?: Options,
): PackageDefinition => {
  wrappers['.google.protobuf.Timestamp'] = {
    fromObject: function (value) {
      return {
        seconds: value.getTime() / 1000,
        nanos: (value.getTime() % 1000) * 1e6,
      };
    },
    toObject: function (message: { seconds: number; nanos: number }, options) {
      return new Date(message.seconds * 1000 + message.nanos / 1e6);
    },
  } as any;

  return _loadSync(filename, options);
};

this package can then be used in nest, by setting the protoLoader setting to the name of the package Not sure if this makes sense to implement in this package though, since it’s tackling a problem very specific to nest…

Could we get option to turn off ts-proto flattening wrapper types?

Any updates on this? Is it possible to have an option that will generate for example for an optional

google.protobuf.Int64Value propertyId = 1

to generate a typescript as follows:

propertyId?: Int64Value;

I’ve manually set the property to this type and it works as expected. Although I have to manually check if the input property has value sent the Int64Value object and send null if otherwise.

@tomer-friedman No we are just avoiding using wrapper types for now.