diff --git a/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint-test.ts b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint-test.ts new file mode 100644 index 000000000..abbd1bc1e --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint-test.ts @@ -0,0 +1,110 @@ +import { FieldOption } from "./fieldoption-jstype-with-forcelong-bigint"; +import { hexToUint8Array, uint8ArrayToHex } from "../utils"; + +describe("FieldOption jstype with ForceLong bigint", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: FieldOption = { + normalField: BigInt(123), + numberField: 456, + stringField: "789", + }; + + const writer = FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("087b10c803189506"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("087b10c803189506"); + + const decodedMessage = FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: BigInt(123), + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: BigInt(123), + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: FieldOption = { + normalField: BigInt(123), + numberField: 456, + stringField: "789", + }; + + const json = FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: "123", + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = FieldOption.create(); + + expect(message).toEqual({ + normalField: BigInt(0), + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = FieldOption.create({ + normalField: BigInt(123), + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: BigInt(123), + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = FieldOption.fromPartial({ + normalField: BigInt(123), + stringField: "789", + }); + + expect(partial).toEqual({ + normalField: BigInt(123), + numberField: 0, + stringField: "789", + }); + }); + }); +}); diff --git a/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.bin b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.bin new file mode 100644 index 000000000..e421dd780 Binary files /dev/null and b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.bin differ diff --git a/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.proto b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.proto new file mode 100644 index 000000000..43ac526e1 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package foo; + +message FieldOption { + int64 normalField = 1 [jstype = JS_NORMAL]; + int64 numberField = 2 [jstype = JS_NUMBER]; + int64 stringField = 3 [jstype = JS_STRING]; +} diff --git a/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.ts b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.ts new file mode 100644 index 000000000..46343a7f9 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.ts @@ -0,0 +1,145 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; +import Long = require("long"); + +export const protobufPackage = "foo"; + +export interface FieldOption { + normalField: bigint; + numberField: number; + stringField: string; +} + +function createBaseFieldOption(): FieldOption { + return { normalField: BigInt("0"), numberField: 0, stringField: "0" }; +} + +export const FieldOption = { + encode(message: FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.normalField !== BigInt("0")) { + if (BigInt.asIntN(64, message.normalField) !== message.normalField) { + throw new globalThis.Error("value provided for field message.normalField of type int64 too large"); + } + writer.uint32(8).int64(message.normalField.toString()); + } + if (message.numberField !== 0) { + if (BigInt.asIntN(64, BigInt(message.numberField)) !== BigInt(message.numberField)) { + throw new globalThis.Error("value provided for field BigInt(message.numberField) of type int64 too large"); + } + writer.uint32(16).int64(BigInt(message.numberField).toString()); + } + if (message.stringField !== "0") { + if (BigInt.asIntN(64, BigInt(message.stringField)) !== BigInt(message.stringField)) { + throw new globalThis.Error("value provided for field BigInt(message.stringField) of type int64 too large"); + } + writer.uint32(24).int64(BigInt(message.stringField).toString()); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.normalField = longToBigint(reader.int64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.numberField = longToNumber(reader.int64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.stringField = longToString(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FieldOption { + return { + normalField: isSet(object.normalField) ? BigInt(object.normalField) : BigInt("0"), + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: FieldOption): unknown { + const obj: any = {}; + if (message.normalField !== BigInt("0")) { + obj.normalField = message.normalField.toString(); + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): FieldOption { + return FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): FieldOption { + const message = createBaseFieldOption(); + message.normalField = object.normalField ?? BigInt("0"); + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | bigint | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +function longToString(long: Long) { + return long.toString(); +} + +function longToBigint(long: Long) { + return BigInt(long.toString()); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/fieldoption-jstype-with-forcelong-bigint/jest.config.js b/integration/fieldoption-jstype-with-forcelong-bigint/jest.config.js new file mode 100644 index 000000000..fa47ebdca --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-bigint/jest.config.js @@ -0,0 +1,8 @@ +const base = require("../../jest.config"); + +// Set maxWorkers for assertion failures involving BigInt +// https://github.com/jestjs/jest/issues/11617 +module.exports = { + ...base, + maxWorkers: 1, +}; diff --git a/integration/fieldoption-jstype-with-forcelong-bigint/parameters.txt b/integration/fieldoption-jstype-with-forcelong-bigint/parameters.txt new file mode 100644 index 000000000..5321d6357 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-bigint/parameters.txt @@ -0,0 +1 @@ +forceLong=bigint,useJsTypeOverride=true diff --git a/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long-test.ts b/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long-test.ts new file mode 100644 index 000000000..a7f10208b --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long-test.ts @@ -0,0 +1,112 @@ +import { FieldOption } from "./fieldoption-jstype-with-forcelong-long"; +import { hexToUint8Array, uint8ArrayToHex } from "../utils"; +// @ts-ignore +import Long = require("long"); + +describe("FieldOption jstype with ForceLong long", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: FieldOption = { + normalField: Long.fromValue(123), + numberField: 456, + stringField: "789", + }; + + const writer = FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("087b10c803189506"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("087b10c803189506"); + + const decodedMessage = FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: Long.fromValue(123), + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: Long.fromValue(123), + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: FieldOption = { + normalField: Long.fromValue(123), + numberField: 456, + stringField: "789", + }; + + const json = FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: "123", + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = FieldOption.create(); + + expect(message).toEqual({ + normalField: Long.fromValue(0), + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = FieldOption.create({ + normalField: Long.fromValue(123), + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: Long.fromValue(123), + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = FieldOption.fromPartial({ + normalField: Long.fromValue(123), + stringField: "789", + }); + + expect(partial).toEqual({ + normalField: Long.fromValue(123), + numberField: 0, + stringField: "789", + }); + }); + }); +}); diff --git a/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.bin b/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.bin new file mode 100644 index 000000000..8f193c9e5 Binary files /dev/null and b/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.bin differ diff --git a/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.proto b/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.proto new file mode 100644 index 000000000..43ac526e1 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package foo; + +message FieldOption { + int64 normalField = 1 [jstype = JS_NORMAL]; + int64 numberField = 2 [jstype = JS_NUMBER]; + int64 stringField = 3 [jstype = JS_STRING]; +} diff --git a/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.ts b/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.ts new file mode 100644 index 000000000..5b30ea8ee --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-long/fieldoption-jstype-with-forcelong-long.ts @@ -0,0 +1,134 @@ +/* eslint-disable */ +import Long = require("long"); +import * as _m0 from "protobufjs/minimal"; + +export const protobufPackage = "foo"; + +export interface FieldOption { + normalField: Long; + numberField: number; + stringField: string; +} + +function createBaseFieldOption(): FieldOption { + return { normalField: Long.ZERO, numberField: 0, stringField: "0" }; +} + +export const FieldOption = { + encode(message: FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (!message.normalField.equals(Long.ZERO)) { + writer.uint32(8).int64(message.normalField); + } + if (message.numberField !== 0) { + writer.uint32(16).int64(message.numberField); + } + if (message.stringField !== "0") { + writer.uint32(24).int64(message.stringField); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.normalField = reader.int64() as Long; + continue; + case 2: + if (tag !== 16) { + break; + } + + message.numberField = longToNumber(reader.int64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.stringField = longToString(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FieldOption { + return { + normalField: isSet(object.normalField) ? Long.fromValue(object.normalField) : Long.ZERO, + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: FieldOption): unknown { + const obj: any = {}; + if (!message.normalField.equals(Long.ZERO)) { + obj.normalField = (message.normalField || Long.ZERO).toString(); + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): FieldOption { + return FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): FieldOption { + const message = createBaseFieldOption(); + message.normalField = (object.normalField !== undefined && object.normalField !== null) + ? Long.fromValue(object.normalField) + : Long.ZERO; + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Long ? string | number | Long : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +function longToString(long: Long) { + return long.toString(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/fieldoption-jstype-with-forcelong-long/parameters.txt b/integration/fieldoption-jstype-with-forcelong-long/parameters.txt new file mode 100644 index 000000000..ecb8f650a --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-long/parameters.txt @@ -0,0 +1 @@ +forceLong=long,useJsTypeOverride=true diff --git a/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number-test.ts b/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number-test.ts new file mode 100644 index 000000000..166b96646 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number-test.ts @@ -0,0 +1,110 @@ +import { FieldOption } from "./fieldoption-jstype-with-forcelong-number"; +import { hexToUint8Array, uint8ArrayToHex } from "../utils"; + +describe("FieldOption jstype with ForceLong number", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const writer = FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("087b10c803189506"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("087b10c803189506"); + + const decodedMessage = FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const json = FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = FieldOption.create(); + + expect(message).toEqual({ + normalField: 0, + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = FieldOption.create({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = FieldOption.fromPartial({ + normalField: 123, + stringField: "789", + }); + + expect(partial).toEqual({ + normalField: 123, + numberField: 0, + stringField: "789", + }); + }); + }); +}); diff --git a/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.bin b/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.bin new file mode 100644 index 000000000..c5596d871 Binary files /dev/null and b/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.bin differ diff --git a/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.proto b/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.proto new file mode 100644 index 000000000..43ac526e1 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package foo; + +message FieldOption { + int64 normalField = 1 [jstype = JS_NORMAL]; + int64 numberField = 2 [jstype = JS_NUMBER]; + int64 stringField = 3 [jstype = JS_STRING]; +} diff --git a/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.ts b/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.ts new file mode 100644 index 000000000..7933223b3 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-number/fieldoption-jstype-with-forcelong-number.ts @@ -0,0 +1,132 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; +import Long = require("long"); + +export const protobufPackage = "foo"; + +export interface FieldOption { + normalField: number; + numberField: number; + stringField: string; +} + +function createBaseFieldOption(): FieldOption { + return { normalField: 0, numberField: 0, stringField: "0" }; +} + +export const FieldOption = { + encode(message: FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.normalField !== 0) { + writer.uint32(8).int64(message.normalField); + } + if (message.numberField !== 0) { + writer.uint32(16).int64(message.numberField); + } + if (message.stringField !== "0") { + writer.uint32(24).int64(message.stringField); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.normalField = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.numberField = longToNumber(reader.int64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.stringField = longToString(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FieldOption { + return { + normalField: isSet(object.normalField) ? globalThis.Number(object.normalField) : 0, + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: FieldOption): unknown { + const obj: any = {}; + if (message.normalField !== 0) { + obj.normalField = Math.round(message.normalField); + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): FieldOption { + return FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): FieldOption { + const message = createBaseFieldOption(); + message.normalField = object.normalField ?? 0; + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +function longToString(long: Long) { + return long.toString(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/fieldoption-jstype-with-forcelong-number/parameters.txt b/integration/fieldoption-jstype-with-forcelong-number/parameters.txt new file mode 100644 index 000000000..574c6b8e0 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-number/parameters.txt @@ -0,0 +1 @@ +forceLong=number,useJsTypeOverride=true diff --git a/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string-test.ts b/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string-test.ts new file mode 100644 index 000000000..cbfdf414d --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string-test.ts @@ -0,0 +1,110 @@ +import { FieldOption } from "./fieldoption-jstype-with-forcelong-string"; +import { hexToUint8Array, uint8ArrayToHex } from "../utils"; + +describe("FieldOption jstype with ForceLong string", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: FieldOption = { + normalField: "123", + numberField: 456, + stringField: "789", + }; + + const writer = FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("087b10c803189506"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("087b10c803189506"); + + const decodedMessage = FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: "123", + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: "123", + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: FieldOption = { + normalField: "123", + numberField: 456, + stringField: "789", + }; + + const json = FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: "123", + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = FieldOption.create(); + + expect(message).toEqual({ + normalField: "0", + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = FieldOption.create({ + normalField: "123", + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: "123", + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = FieldOption.fromPartial({ + normalField: "123", + numberField: 456, + }); + + expect(partial).toEqual({ + normalField: "123", + numberField: 456, + stringField: "0", + }); + }); + }); +}); diff --git a/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.bin b/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.bin new file mode 100644 index 000000000..de0339e64 Binary files /dev/null and b/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.bin differ diff --git a/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.proto b/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.proto new file mode 100644 index 000000000..43ac526e1 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package foo; + +message FieldOption { + int64 normalField = 1 [jstype = JS_NORMAL]; + int64 numberField = 2 [jstype = JS_NUMBER]; + int64 stringField = 3 [jstype = JS_STRING]; +} diff --git a/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.ts b/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.ts new file mode 100644 index 000000000..9ea0d6248 --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-string/fieldoption-jstype-with-forcelong-string.ts @@ -0,0 +1,132 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; +import Long = require("long"); + +export const protobufPackage = "foo"; + +export interface FieldOption { + normalField: string; + numberField: number; + stringField: string; +} + +function createBaseFieldOption(): FieldOption { + return { normalField: "0", numberField: 0, stringField: "0" }; +} + +export const FieldOption = { + encode(message: FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.normalField !== "0") { + writer.uint32(8).int64(message.normalField); + } + if (message.numberField !== 0) { + writer.uint32(16).int64(message.numberField); + } + if (message.stringField !== "0") { + writer.uint32(24).int64(message.stringField); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.normalField = longToString(reader.int64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.numberField = longToNumber(reader.int64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.stringField = longToString(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FieldOption { + return { + normalField: isSet(object.normalField) ? globalThis.String(object.normalField) : "0", + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: FieldOption): unknown { + const obj: any = {}; + if (message.normalField !== "0") { + obj.normalField = message.normalField; + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): FieldOption { + return FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): FieldOption { + const message = createBaseFieldOption(); + message.normalField = object.normalField ?? "0"; + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +function longToString(long: Long) { + return long.toString(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/fieldoption-jstype-with-forcelong-string/parameters.txt b/integration/fieldoption-jstype-with-forcelong-string/parameters.txt new file mode 100644 index 000000000..a81fb911a --- /dev/null +++ b/integration/fieldoption-jstype-with-forcelong-string/parameters.txt @@ -0,0 +1 @@ +forceLong=string,useJsTypeOverride=true diff --git a/integration/fieldoption-jstype/fieldoption-jstype-test.ts b/integration/fieldoption-jstype/fieldoption-jstype-test.ts new file mode 100644 index 000000000..50354e081 --- /dev/null +++ b/integration/fieldoption-jstype/fieldoption-jstype-test.ts @@ -0,0 +1,550 @@ +import { + Fixed64FieldOption, + Int64FieldOption, + SFixed64FieldOption, + SInt64FieldOption, + UInt64FieldOption, +} from "./fieldoption-jstype"; +import { hexToUint8Array, uint8ArrayToHex } from "../utils"; + +describe("FieldOption jstype", () => { + describe("Int64", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: Int64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const writer = Int64FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("087b10c803189506"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("087b10c803189506"); + + const decodedMessage = Int64FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = Int64FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: Int64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const json = Int64FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = Int64FieldOption.create(); + + expect(message).toEqual({ + normalField: 0, + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = Int64FieldOption.create({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = Int64FieldOption.fromPartial({ + normalField: 123, + stringField: "789", + }); + + expect(partial).toEqual({ + normalField: 123, + numberField: 0, + stringField: "789", + }); + }); + }); + }); + + describe("UInt64", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: UInt64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const writer = UInt64FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("087b10c803189506"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("087b10c803189506"); + + const decodedMessage = UInt64FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = UInt64FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: UInt64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const json = UInt64FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = UInt64FieldOption.create(); + + expect(message).toEqual({ + normalField: 0, + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = UInt64FieldOption.create({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = UInt64FieldOption.fromPartial({ + normalField: 123, + stringField: "789", + }); + + expect(partial).toEqual({ + normalField: 123, + numberField: 0, + stringField: "789", + }); + }); + }); + }); + + describe("SInt64", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: SInt64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const writer = SInt64FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("08f60110900718aa0c"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("08f60110900718aa0c"); + + const decodedMessage = SInt64FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = SInt64FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: SInt64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const json = SInt64FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = SInt64FieldOption.create(); + + expect(message).toEqual({ + normalField: 0, + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = SInt64FieldOption.create({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = SInt64FieldOption.fromPartial({ + normalField: 123, + stringField: "789", + }); + + expect(partial).toEqual({ + normalField: 123, + numberField: 0, + stringField: "789", + }); + }); + }); + }); + + describe("Fixed64", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: Fixed64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const writer = Fixed64FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("097b0000000000000011c801000000000000191503000000000000"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("097b0000000000000011c801000000000000191503000000000000"); + + const decodedMessage = Fixed64FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = Fixed64FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: Fixed64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const json = Fixed64FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = Fixed64FieldOption.create(); + + expect(message).toEqual({ + normalField: 0, + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = Fixed64FieldOption.create({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = Fixed64FieldOption.fromPartial({ + normalField: 123, + stringField: "789", + }); + + expect(partial).toEqual({ + normalField: 123, + numberField: 0, + stringField: "789", + }); + }); + }); + }); + + describe("SFixed64", () => { + describe("encode", () => { + it("should encode the message", () => { + const message: SFixed64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const writer = SFixed64FieldOption.encode(message); + const buffer = writer.finish(); + + expect(uint8ArrayToHex(buffer)).toEqual("097b0000000000000011c801000000000000191503000000000000"); + }); + }); + + describe("decode", () => { + it("should decode the message", () => { + const buffer = hexToUint8Array("097b0000000000000011c801000000000000191503000000000000"); + + const decodedMessage = SFixed64FieldOption.decode(buffer); + + expect(decodedMessage).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromJSON", () => { + it("should create a message from JSON", () => { + const json = { + normalField: "123", + numberField: "456", + stringField: "789", + }; + + const message = SFixed64FieldOption.fromJSON(json); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("toJSON", () => { + it("should convert the message to JSON", () => { + const message: SFixed64FieldOption = { + normalField: 123, + numberField: 456, + stringField: "789", + }; + + const json = SFixed64FieldOption.toJSON(message); + + expect(json).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("create", () => { + it("should create a message with default values", () => { + const message = SFixed64FieldOption.create(); + + expect(message).toEqual({ + normalField: 0, + numberField: 0, + stringField: "0", + }); + }); + + it("should create a message with provided values", () => { + const message = SFixed64FieldOption.create({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + + expect(message).toEqual({ + normalField: 123, + numberField: 456, + stringField: "789", + }); + }); + }); + + describe("fromPartial", () => { + it("should create a message from a partial object", () => { + const partial = SFixed64FieldOption.fromPartial({ + normalField: 123, + stringField: "789", + }); + + expect(partial).toEqual({ + normalField: 123, + numberField: 0, + stringField: "789", + }); + }); + }); + }); +}); diff --git a/integration/fieldoption-jstype/fieldoption-jstype.bin b/integration/fieldoption-jstype/fieldoption-jstype.bin new file mode 100644 index 000000000..ef344ee83 Binary files /dev/null and b/integration/fieldoption-jstype/fieldoption-jstype.bin differ diff --git a/integration/fieldoption-jstype/fieldoption-jstype.proto b/integration/fieldoption-jstype/fieldoption-jstype.proto new file mode 100644 index 000000000..c4a69ccd1 --- /dev/null +++ b/integration/fieldoption-jstype/fieldoption-jstype.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; + +package int64; + +message Int64FieldOption { + int64 normalField = 1 [jstype = JS_NORMAL]; + int64 numberField = 2 [jstype = JS_NUMBER]; + int64 stringField = 3 [jstype = JS_STRING]; +} + +message UInt64FieldOption { + uint64 normalField = 1 [jstype = JS_NORMAL]; + uint64 numberField = 2 [jstype = JS_NUMBER]; + uint64 stringField = 3 [jstype = JS_STRING]; +} + +message SInt64FieldOption { + sint64 normalField = 1 [jstype = JS_NORMAL]; + sint64 numberField = 2 [jstype = JS_NUMBER]; + sint64 stringField = 3 [jstype = JS_STRING]; +} + +message Fixed64FieldOption { + fixed64 normalField = 1 [jstype = JS_NORMAL]; + fixed64 numberField = 2 [jstype = JS_NUMBER]; + fixed64 stringField = 3 [jstype = JS_STRING]; +} + +message SFixed64FieldOption { + sfixed64 normalField = 1 [jstype = JS_NORMAL]; + sfixed64 numberField = 2 [jstype = JS_NUMBER]; + sfixed64 stringField = 3 [jstype = JS_STRING]; +} diff --git a/integration/fieldoption-jstype/fieldoption-jstype.ts b/integration/fieldoption-jstype/fieldoption-jstype.ts new file mode 100644 index 000000000..d9b73bd63 --- /dev/null +++ b/integration/fieldoption-jstype/fieldoption-jstype.ts @@ -0,0 +1,512 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; +import Long = require("long"); + +export const protobufPackage = "int64"; + +export interface Int64FieldOption { + normalField: number; + numberField: number; + stringField: string; +} + +export interface UInt64FieldOption { + normalField: number; + numberField: number; + stringField: string; +} + +export interface SInt64FieldOption { + normalField: number; + numberField: number; + stringField: string; +} + +export interface Fixed64FieldOption { + normalField: number; + numberField: number; + stringField: string; +} + +export interface SFixed64FieldOption { + normalField: number; + numberField: number; + stringField: string; +} + +function createBaseInt64FieldOption(): Int64FieldOption { + return { normalField: 0, numberField: 0, stringField: "0" }; +} + +export const Int64FieldOption = { + encode(message: Int64FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.normalField !== 0) { + writer.uint32(8).int64(message.normalField); + } + if (message.numberField !== 0) { + writer.uint32(16).int64(message.numberField); + } + if (message.stringField !== "0") { + writer.uint32(24).int64(message.stringField); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Int64FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseInt64FieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.normalField = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.numberField = longToNumber(reader.int64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.stringField = longToString(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Int64FieldOption { + return { + normalField: isSet(object.normalField) ? globalThis.Number(object.normalField) : 0, + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: Int64FieldOption): unknown { + const obj: any = {}; + if (message.normalField !== 0) { + obj.normalField = Math.round(message.normalField); + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): Int64FieldOption { + return Int64FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Int64FieldOption { + const message = createBaseInt64FieldOption(); + message.normalField = object.normalField ?? 0; + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +function createBaseUInt64FieldOption(): UInt64FieldOption { + return { normalField: 0, numberField: 0, stringField: "0" }; +} + +export const UInt64FieldOption = { + encode(message: UInt64FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.normalField !== 0) { + writer.uint32(8).uint64(message.normalField); + } + if (message.numberField !== 0) { + writer.uint32(16).uint64(message.numberField); + } + if (message.stringField !== "0") { + writer.uint32(24).uint64(message.stringField); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UInt64FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUInt64FieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.normalField = longToNumber(reader.uint64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.numberField = longToNumber(reader.uint64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.stringField = longToString(reader.uint64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): UInt64FieldOption { + return { + normalField: isSet(object.normalField) ? globalThis.Number(object.normalField) : 0, + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: UInt64FieldOption): unknown { + const obj: any = {}; + if (message.normalField !== 0) { + obj.normalField = Math.round(message.normalField); + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): UInt64FieldOption { + return UInt64FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UInt64FieldOption { + const message = createBaseUInt64FieldOption(); + message.normalField = object.normalField ?? 0; + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +function createBaseSInt64FieldOption(): SInt64FieldOption { + return { normalField: 0, numberField: 0, stringField: "0" }; +} + +export const SInt64FieldOption = { + encode(message: SInt64FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.normalField !== 0) { + writer.uint32(8).sint64(message.normalField); + } + if (message.numberField !== 0) { + writer.uint32(16).sint64(message.numberField); + } + if (message.stringField !== "0") { + writer.uint32(24).sint64(message.stringField); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SInt64FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSInt64FieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.normalField = longToNumber(reader.sint64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.numberField = longToNumber(reader.sint64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.stringField = longToString(reader.sint64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SInt64FieldOption { + return { + normalField: isSet(object.normalField) ? globalThis.Number(object.normalField) : 0, + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: SInt64FieldOption): unknown { + const obj: any = {}; + if (message.normalField !== 0) { + obj.normalField = Math.round(message.normalField); + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): SInt64FieldOption { + return SInt64FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): SInt64FieldOption { + const message = createBaseSInt64FieldOption(); + message.normalField = object.normalField ?? 0; + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +function createBaseFixed64FieldOption(): Fixed64FieldOption { + return { normalField: 0, numberField: 0, stringField: "0" }; +} + +export const Fixed64FieldOption = { + encode(message: Fixed64FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.normalField !== 0) { + writer.uint32(9).fixed64(message.normalField); + } + if (message.numberField !== 0) { + writer.uint32(17).fixed64(message.numberField); + } + if (message.stringField !== "0") { + writer.uint32(25).fixed64(message.stringField); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Fixed64FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFixed64FieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 9) { + break; + } + + message.normalField = longToNumber(reader.fixed64() as Long); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.numberField = longToNumber(reader.fixed64() as Long); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.stringField = longToString(reader.fixed64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Fixed64FieldOption { + return { + normalField: isSet(object.normalField) ? globalThis.Number(object.normalField) : 0, + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: Fixed64FieldOption): unknown { + const obj: any = {}; + if (message.normalField !== 0) { + obj.normalField = Math.round(message.normalField); + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): Fixed64FieldOption { + return Fixed64FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Fixed64FieldOption { + const message = createBaseFixed64FieldOption(); + message.normalField = object.normalField ?? 0; + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +function createBaseSFixed64FieldOption(): SFixed64FieldOption { + return { normalField: 0, numberField: 0, stringField: "0" }; +} + +export const SFixed64FieldOption = { + encode(message: SFixed64FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.normalField !== 0) { + writer.uint32(9).sfixed64(message.normalField); + } + if (message.numberField !== 0) { + writer.uint32(17).sfixed64(message.numberField); + } + if (message.stringField !== "0") { + writer.uint32(25).sfixed64(message.stringField); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SFixed64FieldOption { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSFixed64FieldOption(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 9) { + break; + } + + message.normalField = longToNumber(reader.sfixed64() as Long); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.numberField = longToNumber(reader.sfixed64() as Long); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.stringField = longToString(reader.sfixed64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SFixed64FieldOption { + return { + normalField: isSet(object.normalField) ? globalThis.Number(object.normalField) : 0, + numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, + stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", + }; + }, + + toJSON(message: SFixed64FieldOption): unknown { + const obj: any = {}; + if (message.normalField !== 0) { + obj.normalField = Math.round(message.normalField); + } + if (message.numberField !== 0) { + obj.numberField = globalThis.Number(message.numberField); + } + if (message.stringField !== "0") { + obj.stringField = globalThis.String(message.stringField); + } + return obj; + }, + + create, I>>(base?: I): SFixed64FieldOption { + return SFixed64FieldOption.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): SFixed64FieldOption { + const message = createBaseSFixed64FieldOption(); + message.normalField = object.normalField ?? 0; + message.numberField = object.numberField ?? 0; + message.stringField = object.stringField ?? "0"; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +function longToString(long: Long) { + return long.toString(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/fieldoption-jstype/parameters.txt b/integration/fieldoption-jstype/parameters.txt new file mode 100644 index 000000000..52cf52ac0 --- /dev/null +++ b/integration/fieldoption-jstype/parameters.txt @@ -0,0 +1 @@ +useJsTypeOverride=true diff --git a/integration/tsconfig.json b/integration/tsconfig.json index 8e5407fb4..00a1e8b57 100644 --- a/integration/tsconfig.json +++ b/integration/tsconfig.json @@ -10,6 +10,7 @@ "angular", "batching-with-context-esModuleInterop", "import-mapping/mapping.ts", - "simple-esmodule-interop" + "simple-esmodule-interop", + "fieldoption-jstype*" ] } \ No newline at end of file diff --git a/integration/tsconfig.proto.json b/integration/tsconfig.proto.json index 7c2385c47..ccfc001d3 100644 --- a/integration/tsconfig.proto.json +++ b/integration/tsconfig.proto.json @@ -15,6 +15,7 @@ "angular", "batching-with-context-esModuleInterop", "import-mapping/mapping.ts", - "simple-esmodule-interop" + "simple-esmodule-interop", + "fieldoption-jstype*" ] } diff --git a/integration/utils/index.ts b/integration/utils/index.ts new file mode 100644 index 000000000..a7fc4227c --- /dev/null +++ b/integration/utils/index.ts @@ -0,0 +1,18 @@ +export function uint8ArrayToHex(array: Uint8Array): string { + return Array.from(array) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); +} + +export function hexToUint8Array(hexString: string): Uint8Array { + if (hexString.length % 2 !== 0) { + throw new Error("The hex string is of an invalid length."); + } + + let bytes: number[] = []; + for (let i = 0; i < hexString.length; i += 2) { + bytes.push(parseInt(hexString.substring(i, i + 2), 16) as number); + } + + return new Uint8Array(bytes); +} diff --git a/src/main.ts b/src/main.ts index 97d7ecc93..4198072ba 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,6 +5,7 @@ import { FieldDescriptorProto, FieldDescriptorProto_Label, FieldDescriptorProto_Type, + FieldOptions_JSType, FileDescriptorProto, } from "ts-proto-descriptors"; import { camelToSnake, capitalize, maybeSnakeToCamel } from "./case"; @@ -58,12 +59,14 @@ import { defaultValue, detectMapType, getEnumMethod, + getFieldOptionsJsType, isAnyValueType, isBytes, isBytesValueType, isEnum, isFieldMaskType, isFieldMaskTypeName, + isJsTypeFieldOption, isListValueType, isLong, isLongValueType, @@ -1111,7 +1114,16 @@ function getDecodeReadSnippet(ctx: Context, field: FieldDescriptorProto) { readSnippet = code`${readSnippet} as Buffer`; } } else if (basicLongWireType(field.type) !== undefined) { - if (options.forceLong === LongOption.LONG) { + if (isJsTypeFieldOption(options, field)) { + switch (field!.options!.jstype) { + case FieldOptions_JSType.JS_NUMBER: + readSnippet = code`${utils.longToNumber}(${readSnippet} as Long)`; + break; + case FieldOptions_JSType.JS_STRING: + readSnippet = code`${utils.longToString}(${readSnippet} as Long)`; + break; + } + } else if (options.forceLong === LongOption.LONG) { readSnippet = code`${readSnippet} as Long`; } else if (options.forceLong === LongOption.STRING) { readSnippet = code`${utils.longToString}(${readSnippet} as Long)`; @@ -1351,7 +1363,7 @@ function generateDecode(ctx: Context, fullName: string, messageDesc: DescriptorP } /** Returns a generic writer.doSomething based on the basic type */ -function getEncodeWriteSnippet(ctx: Context, field: FieldDescriptorProto): (place: string) => Code { +function getEncodeWriteSnippet(ctx: Context, field: FieldDescriptorProto): (place: string, placeAlt?: string) => Code { const { options, utils } = ctx; if (isEnum(field) && options.stringEnums) { const tag = ((field.number << 3) | basicWireType(field.type)) >>> 0; @@ -1364,13 +1376,13 @@ function getEncodeWriteSnippet(ctx: Context, field: FieldDescriptorProto): (plac case "int64": case "sint64": case "sfixed64": - return (place) => code`if (BigInt.asIntN(64, ${place}) !== ${place}) { + return (place, placeAlt) => code`if (BigInt.asIntN(64, ${place}) !== ${placeAlt ?? place}) { throw new ${utils.globalThis}.Error('value provided for field ${place} of type ${fieldType} too large'); } writer.uint32(${tag}).${toReaderCall(field)}(${place}.toString())`; case "uint64": case "fixed64": - return (place) => code`if (BigInt.asUintN(64, ${place}) !== ${place}) { + return (place, placeAlt) => code`if (BigInt.asUintN(64, ${place}) !== ${placeAlt ?? place}) { throw new ${utils.globalThis}.Error('value provided for field ${place} of type ${fieldType} too large'); } writer.uint32(${tag}).${toReaderCall(field)}(${place}.toString())`; @@ -1613,9 +1625,15 @@ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorP } `); } else if (isScalar(field) || isEnum(field)) { + const isJsType = isScalar(field) && isJsTypeFieldOption(options, field); + const body = + isJsType && options.forceLong === LongOption.BIGINT + ? writeSnippet(`BigInt(${messageProperty})`) + : writeSnippet(`${messageProperty}`); + chunks.push(code` if (${notDefaultCheck(ctx, field, messageDesc.options, `${messageProperty}`)}) { - ${writeSnippet(`${messageProperty}`)}; + ${body}; } `); } else { @@ -1747,6 +1765,7 @@ function generateExtension(ctx: Context, message: DescriptorProto | undefined, e return (place) => code`writer.${toReaderCall(field)}(${place})`; } else if (isObjectId(field) && options.useMongoObjectId) { const type = basicTypeName(ctx, field, { keepValueType: true }); + return (place) => code`${type}.encode(${utils.toProtoObjectId}(${place}), writer.fork()).ldelim()`; } else if ( isTimestamp(field) && @@ -1941,6 +1960,12 @@ function generateFromJson(ctx: Context, fullName: string, fullTypeName: string, } else { return code`${utils.bytesFromBase64}(${from})`; } + } else if (isLong(field) && isJsTypeFieldOption(options, field)) { + const fieldType = getFieldOptionsJsType(field, ctx.options) ?? field.type; + const cstr = capitalize( + basicTypeName(ctx, { ...field, type: fieldType }, { keepValueType: true }).toCodeString([]), + ); + return code`${utils.globalThis}.${cstr}(${from})`; } else if (isLong(field) && options.forceLong === LongOption.LONG) { const cstr = capitalize(basicTypeName(ctx, field, { keepValueType: true }).toCodeString([])); return code`${cstr}.fromValue(${from})`; @@ -2236,6 +2261,16 @@ function generateToJson( return code`${type}.toJSON(${from})`; } else if (isBytes(field)) { return code`${utils.base64FromBytes}(${from})`; + } else if (isLong(field) && isJsTypeFieldOption(options, field)) { + const fieldType = getFieldOptionsJsType(field, ctx.options) ?? field.type; + if (!fieldType) { + return code`${from}`; + } + + const cstr = capitalize( + basicTypeName(ctx, { ...field, type: fieldType }, { keepValueType: true }).toCodeString([]), + ); + return code`${utils.globalThis}.${cstr}(${from})`; } else if (isLong(field) && options.forceLong === LongOption.LONG) { return code`(${from} || ${defaultValue(ctx, field)}).toString()`; } else if (isLong(field) && options.forceLong === LongOption.BIGINT) { @@ -2357,7 +2392,11 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri const objectProperty = getPropertyAccessor("object", fieldName); const readSnippet = (from: string): Code => { - if ((isLong(field) || isLongValueType(field)) && options.forceLong === LongOption.LONG) { + if ( + (isLong(field) || isLongValueType(field)) && + options.forceLong === LongOption.LONG && + !isJsTypeFieldOption(options, field) + ) { return code`Long.fromValue(${from})`; } else if (isObjectId(field) && options.useMongoObjectId) { return code`${from} as mongodb.ObjectId`; diff --git a/src/options.ts b/src/options.ts index b50191cef..9b003fd12 100644 --- a/src/options.ts +++ b/src/options.ts @@ -42,6 +42,7 @@ export type Options = { context: boolean; snakeToCamel: Array<"json" | "keys">; forceLong: LongOption; + useJsTypeOverride: boolean; globalThisPolyfill: boolean; useOptionals: boolean | "none" | "deprecatedOnly" | "messages" | "all"; // boolean is deprecated emitDefaultValues: Array<"json-methods">; @@ -108,6 +109,7 @@ export function defaultOptions(): Options { emitDefaultValues: [], globalThisPolyfill: false, forceLong: LongOption.NUMBER, + useJsTypeOverride: false, useOptionals: "none", useDate: DateOption.DATE, useJsonTimestamp: JsonTimestampOption.RFC3339, diff --git a/src/types.ts b/src/types.ts index 3d986a33c..fe815f9e4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,6 +5,7 @@ import { FieldDescriptorProto, FieldDescriptorProto_Label, FieldDescriptorProto_Type, + FieldOptions_JSType, FileDescriptorProto, MessageOptions, MethodDescriptorProto, @@ -82,7 +83,10 @@ export function basicTypeName( typeOptions: { keepValueType?: boolean } = {}, ): Code { const { options } = ctx; - switch (field.type) { + + const fieldType = getFieldOptionsJsType(field, ctx.options) ?? field.type; + + switch (fieldType) { case FieldDescriptorProto_Type.TYPE_DOUBLE: case FieldDescriptorProto_Type.TYPE_FLOAT: case FieldDescriptorProto_Type.TYPE_INT32: @@ -96,8 +100,10 @@ export function basicTypeName( case FieldDescriptorProto_Type.TYPE_SINT64: case FieldDescriptorProto_Type.TYPE_FIXED64: case FieldDescriptorProto_Type.TYPE_SFIXED64: - // this handles 2^53, Long is only needed for 2^64; this is effectively pbjs's forceNumber - return longTypeName(ctx); + return isJsTypeFieldOption(options, field) + ? jsTypeName(field) ?? longTypeName(ctx) + : // this handles 2^53, Long is only needed for 2^64; this is effectively pbjs's forceNumber + longTypeName(ctx); case FieldDescriptorProto_Type.TYPE_BOOL: return code`boolean`; case FieldDescriptorProto_Type.TYPE_STRING: @@ -184,10 +190,31 @@ export function packedType(type: FieldDescriptorProto_Type): number | undefined } } +export function getFieldOptionsJsType( + field: FieldDescriptorProto, + options: Options, +): FieldDescriptorProto_Type | undefined { + if (!options.useJsTypeOverride || field.options?.jstype === undefined) { + return; + } + + switch (field.options.jstype) { + case FieldOptions_JSType.JS_STRING: + return FieldDescriptorProto_Type.TYPE_STRING; + case FieldOptions_JSType.JS_NUMBER: + return FieldDescriptorProto_Type.TYPE_INT64; + // In the case of JS_NORMAL, we don't want to override the type, so we return + case FieldOptions_JSType.JS_NORMAL: + // In the case of UNRECOGNIZED, we assume default behavior and we don't want to override the type, so we return + case FieldOptions_JSType.UNRECOGNIZED: + return; + } +} + export function defaultValue(ctx: Context, field: FieldDescriptorProto): any { const { typeMap, options, utils, currentFile } = ctx; const useDefaultValue = !currentFile.isProto3Syntax && !options.disableProto2DefaultValues && field.defaultValue; - const numbericDefaultVal = useDefaultValue ? field.defaultValue : 0; + const numericDefaultVal = useDefaultValue ? field.defaultValue : 0; switch (field.type) { case FieldDescriptorProto_Type.TYPE_DOUBLE: case FieldDescriptorProto_Type.TYPE_FLOAT: @@ -196,7 +223,7 @@ export function defaultValue(ctx: Context, field: FieldDescriptorProto): any { case FieldDescriptorProto_Type.TYPE_SINT32: case FieldDescriptorProto_Type.TYPE_FIXED32: case FieldDescriptorProto_Type.TYPE_SFIXED32: - return numbericDefaultVal; + return numericDefaultVal; case FieldDescriptorProto_Type.TYPE_ENUM: // proto3 enforces enums starting at 0, however proto2 does not, so we have // to probe and see if zero is an allowed value. If it's not, pick the first one. @@ -214,32 +241,35 @@ export function defaultValue(ctx: Context, field: FieldDescriptorProto): any { } else { return defaultEnum.number; } + + case FieldDescriptorProto_Type.TYPE_INT64: case FieldDescriptorProto_Type.TYPE_UINT64: case FieldDescriptorProto_Type.TYPE_FIXED64: - if (options.forceLong === LongOption.LONG) { - return code`${utils.Long}.${useDefaultValue ? "fromNumber" : "UZERO"}${ - useDefaultValue ? `(${numbericDefaultVal})` : "" - }`; - } else if (options.forceLong === LongOption.STRING) { - return `"${numbericDefaultVal}"`; - } else if (options.forceLong === LongOption.BIGINT) { - return `BigInt("${numbericDefaultVal}")`; - } else { - return numbericDefaultVal; - } - case FieldDescriptorProto_Type.TYPE_INT64: case FieldDescriptorProto_Type.TYPE_SINT64: case FieldDescriptorProto_Type.TYPE_SFIXED64: + if (isJsTypeFieldOption(options, field)) { + switch (field.options!.jstype) { + case FieldOptions_JSType.JS_STRING: + return `"${numericDefaultVal}"`; + case FieldOptions_JSType.JS_NUMBER: + return numericDefaultVal; + } + } + if (options.forceLong === LongOption.LONG) { - return code`${utils.Long}.${useDefaultValue ? "fromNumber" : "ZERO"}${ - useDefaultValue ? `(${numbericDefaultVal})` : "" + const value = + field.type === FieldDescriptorProto_Type.TYPE_UINT64 || field.type === FieldDescriptorProto_Type.TYPE_FIXED64 + ? "UZERO" + : "ZERO"; + return code`${utils.Long}.${useDefaultValue ? "fromNumber" : value}${ + useDefaultValue ? `(${numericDefaultVal})` : "" }`; } else if (options.forceLong === LongOption.STRING) { - return `"${numbericDefaultVal}"`; + return `"${numericDefaultVal}"`; } else if (options.forceLong === LongOption.BIGINT) { - return `BigInt("${numbericDefaultVal}")`; + return `BigInt("${numericDefaultVal}")`; } else { - return numbericDefaultVal; + return numericDefaultVal; } case FieldDescriptorProto_Type.TYPE_BOOL: return useDefaultValue ? field.defaultValue : false; @@ -301,7 +331,7 @@ export function notDefaultCheck( case FieldDescriptorProto_Type.TYPE_INT64: case FieldDescriptorProto_Type.TYPE_SINT64: case FieldDescriptorProto_Type.TYPE_SFIXED64: - if (options.forceLong === LongOption.LONG) { + if (options.forceLong === LongOption.LONG && !isJsTypeFieldOption(options, field)) { return code`${maybeNotUndefinedAnd} !${place}.equals(${defaultValue(ctx, field)})`; } else { return code`${maybeNotUndefinedAnd} ${place} !== ${defaultValue(ctx, field)}`; @@ -580,6 +610,14 @@ function longTypeName(ctx: Context): Code { } } +function jsTypeName(field: FieldDescriptorProto): Code | undefined { + if (field.options?.jstype === FieldOptions_JSType.JS_STRING) { + return code`string`; + } else if (field.options?.jstype === FieldOptions_JSType.JS_NUMBER) { + return code`number`; + } +} + /** Maps `.some_proto_namespace.Message` to a TypeName. */ export function messageToTypeName( ctx: Context, @@ -641,7 +679,10 @@ export function toTypeName( return type; } - let type = basicTypeName(ctx, field, { keepValueType: false }); + const fieldType = getFieldOptionsJsType(field, ctx.options) ?? field.type; + + const type = basicTypeName(ctx, { ...field, type: fieldType }, { keepValueType: false }); + if (isRepeated(field)) { const mapType = messageDesc ? detectMapType(ctx, messageDesc, field) : false; if (mapType) { @@ -851,3 +892,10 @@ export function detectBatchMethod( function hasSingleRepeatedField(messageDesc: DescriptorProto): boolean { return messageDesc.field.length == 1 && messageDesc.field[0].label === FieldDescriptorProto_Label.LABEL_REPEATED; } + +export function isJsTypeFieldOption(options: Options, field: FieldDescriptorProto): boolean { + return ( + options.useJsTypeOverride && + (field.options?.jstype === FieldOptions_JSType.JS_NUMBER || field.options?.jstype === FieldOptions_JSType.JS_STRING) + ); +} diff --git a/tests/options-test.ts b/tests/options-test.ts index e6e7b2118..4db6fc333 100644 --- a/tests/options-test.ts +++ b/tests/options-test.ts @@ -56,6 +56,7 @@ describe("options", () => { "useAsyncIterable": false, "useDate": "timestamp", "useExactTypes": true, + "useJsTypeOverride": false, "useJsonName": false, "useJsonTimestamp": "rfc3339", "useJsonWireFormat": false,