grpc-web icon indicating copy to clipboard operation
grpc-web copied to clipboard

feat(generator): add force_int64_string option to prevent int64 preci…

Open theahmadshaikh opened this issue 3 months ago • 3 comments

feat(generator): add force_int64_string option to prevent int64 precision loss

Fixes #1229

Problem

Large 64-bit integers lose precision when mapped to JavaScript's number type in gRPC-Web generated TypeScript definitions. This is a well-known issue affecting JavaScript/TypeScript applications that work with large integers.

Example:

message User {
  int64 user_id = 1;
}

Generated TypeScript (current behavior):

getUserId(): number;  // ❌ Precision loss for values > 2^53
setUserId(value: number): void;

Problem demonstration:

const userId = 9024037547368883040;  // Large int64 value
console.log(userId);  // Output: 9024037547368883200 (precision lost!)

Solution

This PR adds a new CLI option force_int64_string=True that forces all int64/uint64 fields to be generated as string type, preserving full precision.

Usage:

protoc --grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext,force_int64_string=True:. your_proto.proto

Generated TypeScript (with fix):

getUserId(): string;  // ✅ Full precision preserved
setUserId(value: string): void;

Implementation Details

  • New CLI option: force_int64_string=True
  • Backward compatible: Existing [jstype = JS_STRING] field-level option still works
  • Comprehensive coverage: Affects all 64-bit integer types (int64, uint64, sint64, fixed64, sfixed64)
  • Type-safe: Maintains TypeScript type safety with string-based integers

Changes Made

  1. Enhanced JSElementType function to check both jstype option and CLI flag
  2. Updated function signatures throughout the call chain to pass the force_int64_string parameter
  3. Added GeneratorOptions support for parsing the new CLI option
  4. Added test proto file to verify functionality

Testing

Default behavior: Normal fields → number, [jstype = JS_STRING]string
force_int64_string=True: All int64 fields → string
Backward compatibility: Existing behavior preserved

Migration Guide

For new projects:

# Use the new option to prevent precision loss
protoc --grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext,force_int64_string=True:. your_proto.proto

For existing projects:

  • Continue using [jstype = JS_STRING] for specific fields
  • Or migrate to force_int64_string=True for global control

theahmadshaikh avatar Sep 18 '25 11:09 theahmadshaikh

CLA Signed

The committers listed above are authorized under a signed CLA.

  • :white_check_mark: login: theahmadshaikh / name: Ahmad Shaikh (6bd7e465e21ffb767e10cec9e356d07f8b08fd40, d1f678093cb7db5ecdad0ab5aece84ed5bf9bff0, f903d112a07c2d54303cb078ca07ead9d8b9a78b)

@theahmadshaikh Thanks for the contrib PR to help solve this issue!

@Vuhag123 Do you mind helping take a look here?

sampajano avatar Oct 02 '25 22:10 sampajano

Hi,

You're absolutely correct. After further investigation, I realize this PR only changes the TypeScript definitions, not the underlying JavaScript behavior.

The Issue

The gRPC-Web generator (protoc-gen-grpc-web) generates:

  1. Service client stubs (.js files)
  2. TypeScript definitions for protobuf messages (.d.ts files)

But it does NOT generate the actual protobuf message JavaScript implementation - that comes from protoc --js_out (the standard protobuf JavaScript compiler).

What This PR Actually Does

This PR makes the TypeScript definitions correctly reflect what happens when users add [jstype = JS_STRING] to their proto fields. The underlying JavaScript protobuf library (google-protobuf) already respects this option, but the TypeScript definitions weren't being generated correctly by gRPC-Web.

The Real Solution

For the actual runtime fix, users need to:

  1. Add [jstype = JS_STRING] to their int64 fields in the .proto file
  2. The standard protoc --js_out compiler will generate JavaScript code that uses strings
  3. Our PR ensures the TypeScript definitions match that behavior

This PR's Value

  • Provides correct TypeScript types when [jstype = JS_STRING] is used
  • Adds force_int64_string option to automatically apply string types to all int64 fields in TypeScript definitions
  • Ensures TypeScript type checking matches the actual JavaScript behavior

However, you're right that this doesn't "fix" the precision loss on its own - users still need to use [jstype = JS_STRING] in their proto files for the actual JavaScript fix.

Should I update the PR description to clarify this, or would you prefer a different approach?

Thanks for catching this!

theahmadshaikh avatar Oct 06 '25 06:10 theahmadshaikh