Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translating int64 to TypeScript Number type sacrifices accuracy #17

Closed
johanbrandhorst opened this issue Aug 4, 2017 · 11 comments
Closed

Comments

@johanbrandhorst
Copy link
Contributor

The TypeScript Number type is a floating point number, which means attempting to express the int64 range in numbers will lead to inaccuracies. The protoc compiler therefore uses strings when expressing large numeric types in JSON.

Perhaps something similar should be considered for the TypeScript type translation?

@johanbrandhorst johanbrandhorst changed the title Translation int64 to TypeScript Number type sacrifices accuracy Translating int64 to TypeScript Number type sacrifices accuracy Aug 4, 2017
@jonnyreeves
Copy link
Contributor

The protoc compiler therefore uses strings when expressing large numeric types in JSON.

I was under the impression that protoc only ever used javascript's Number type to express int64 values. protocolbuffers/protobuf#2214 looks to address this but has not landed.

@johanbrandhorst
Copy link
Contributor Author

I'm talking specifically about the JSON representation of int64 integers. The JS library may well be using the Number type, so this might be impossible to remedy here, but I thought it'd be good to have this discussion anyway.

@ghost
Copy link

ghost commented Aug 23, 2017

Hi @johanbrandhorst I've looked into this in the past and currently there is no official support in the public version of protobuf to use strings to preserve precision. There are a couple of solutions in progress though:

  • There's this PR which would fix this bug in the JS implementation: JavaScript: Use strings for 64-bit ints to preserve precision protocolbuffers/protobuf#1832 (which if landed would mean ts-protoc-gen should be updated to accept string | number in int64 setters)
  • There's also the field annotation google.protobuf.FieldOptions.JSType which lets you choose per field whether it should use strings or numbers (unfortunately there's no plan to make string the default representation).

Both of these would fix the underlying problem that, although you can use the field setters with a string, just before they're serialised the values are converted to numbers and so precision is lost. There are functions to serialise string encoded int64s but these aren't used in the public version of protobuf (the Google internal version support the JSType annotation and if set to string uses writeInt64String instead of writeInt64)

Although it's not used in generated code, the writeInt64String method is exposed in the protobuf library so there is a hack to make this work:

import { BinaryWriter, BinaryReader } from 'google-protobuf';

Object.defineProperty(BinaryWriter.prototype, 'writeInt64', {
  value: function(field: number, value: number | string) {
    return BinaryWriter.prototype.writeInt64String.call(this, field, value.toString());
  },
});
Object.defineProperty(BinaryReader.prototype, 'readInt64', { value: BinaryReader.prototype.readInt64String });

This is ugly (and doesn't work with the ts-protoc-gen generated types without using something like pb.setMyInt64Field('1309150421589' as any)) but does mean that values are correctly serialised and deserialised as strings

@johanbrandhorst
Copy link
Contributor Author

@improbable-bradley that's a great writeup, I'll reference this in the future, thanks!

@stale
Copy link

stale bot commented May 21, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label May 21, 2018
@johanbrandhorst
Copy link
Contributor Author

protoc has supported this since 3.4.0: https://github.com/google/protobuf/releases/tag/v3.4.0

@stale stale bot removed the wontfix label May 21, 2018
@jonnyreeves
Copy link
Contributor

jonnyreeves commented May 21, 2018 via email

@johanbrandhorst
Copy link
Contributor Author

From protocolbuffers/protobuf#3666 (comment):

message dummy {
    uint64 bigInt = 1 [jstype = JS_STRING];
}

It makes use of the various FromString methods: https://github.com/google/protobuf/blob/dd2dc0f14f9e5dc4e8343cc5f78d5f0bddd8991c/js/binary/arith.js#L289, https://github.com/google/protobuf/blob/dd2dc0f14f9e5dc4e8343cc5f78d5f0bddd8991c/js/binary/encoder.js#L356 etc. I implemented support for this in my GopherJS gRPC-Web library recently: johanbrandhorst/protobuf@7e36e40.

@jonnyreeves
Copy link
Contributor

Thanks @johanbrandhorst; it would be good to add a test-case which uses this field option and provide a mention to it in the README at which point this issue can be closed :)

cc @MarcusLongmuir

@jonnyreeves
Copy link
Contributor

Thanks to @easyCZ's efforts in #80 we are now consistently using protoc v3.5.1 so we should be in a position to test and document this.

@cmoad
Copy link

cmoad commented Aug 30, 2018

We use the jstype = JS_STRING field annotation extensively and are currently working to translate a project from flow to typescript. I forked the protoc-gen-flow project to simply output string instead of number for these cases and it worked fine. The generated JS will use the string variants of the conversion functions, so the *.d.ts files will actually be correct when they say string.

Here is a link of the changes required for the flow project. We'll be attempting the same for this project.
steckel/protoc-gen-flow@master...andcostello:master

jonny-improbable added a commit that referenced this issue Aug 31, 2018
Required an upgrade to google-protobuf 3.6.1, otherwise very straight forward.

Fixes #17
jonny-improbable pushed a commit that referenced this issue Sep 1, 2018
* Add support for jstype annotation

Required an upgrade to google-protobuf 3.6.1, otherwise very straight forward.

Fixes #17

* Add test coverage for all support field types
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants