Skip to content

Commit 0dd951b

Browse files
authored
feat: Add js type support (#1030)
Closes #958 --------- Co-authored-by: Daniel Santiago <[email protected]>
1 parent bd56caf commit 0dd951b

33 files changed

+2271
-32
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { FieldOption } from "./fieldoption-jstype-with-forcelong-bigint";
2+
import { hexToUint8Array, uint8ArrayToHex } from "../utils";
3+
4+
describe("FieldOption jstype with ForceLong bigint", () => {
5+
describe("encode", () => {
6+
it("should encode the message", () => {
7+
const message: FieldOption = {
8+
normalField: BigInt(123),
9+
numberField: 456,
10+
stringField: "789",
11+
};
12+
13+
const writer = FieldOption.encode(message);
14+
const buffer = writer.finish();
15+
16+
expect(uint8ArrayToHex(buffer)).toEqual("087b10c803189506");
17+
});
18+
});
19+
20+
describe("decode", () => {
21+
it("should decode the message", () => {
22+
const buffer = hexToUint8Array("087b10c803189506");
23+
24+
const decodedMessage = FieldOption.decode(buffer);
25+
26+
expect(decodedMessage).toEqual({
27+
normalField: BigInt(123),
28+
numberField: 456,
29+
stringField: "789",
30+
});
31+
});
32+
});
33+
34+
describe("fromJSON", () => {
35+
it("should create a message from JSON", () => {
36+
const json = {
37+
normalField: "123",
38+
numberField: "456",
39+
stringField: "789",
40+
};
41+
42+
const message = FieldOption.fromJSON(json);
43+
44+
expect(message).toEqual({
45+
normalField: BigInt(123),
46+
numberField: 456,
47+
stringField: "789",
48+
});
49+
});
50+
});
51+
52+
describe("toJSON", () => {
53+
it("should convert the message to JSON", () => {
54+
const message: FieldOption = {
55+
normalField: BigInt(123),
56+
numberField: 456,
57+
stringField: "789",
58+
};
59+
60+
const json = FieldOption.toJSON(message);
61+
62+
expect(json).toEqual({
63+
normalField: "123",
64+
numberField: 456,
65+
stringField: "789",
66+
});
67+
});
68+
});
69+
70+
describe("create", () => {
71+
it("should create a message with default values", () => {
72+
const message = FieldOption.create();
73+
74+
expect(message).toEqual({
75+
normalField: BigInt(0),
76+
numberField: 0,
77+
stringField: "0",
78+
});
79+
});
80+
81+
it("should create a message with provided values", () => {
82+
const message = FieldOption.create({
83+
normalField: BigInt(123),
84+
numberField: 456,
85+
stringField: "789",
86+
});
87+
88+
expect(message).toEqual({
89+
normalField: BigInt(123),
90+
numberField: 456,
91+
stringField: "789",
92+
});
93+
});
94+
});
95+
96+
describe("fromPartial", () => {
97+
it("should create a message from a partial object", () => {
98+
const partial = FieldOption.fromPartial({
99+
normalField: BigInt(123),
100+
stringField: "789",
101+
});
102+
103+
expect(partial).toEqual({
104+
normalField: BigInt(123),
105+
numberField: 0,
106+
stringField: "789",
107+
});
108+
});
109+
});
110+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
syntax = "proto3";
2+
3+
package foo;
4+
5+
message FieldOption {
6+
int64 normalField = 1 [jstype = JS_NORMAL];
7+
int64 numberField = 2 [jstype = JS_NUMBER];
8+
int64 stringField = 3 [jstype = JS_STRING];
9+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/* eslint-disable */
2+
import * as _m0 from "protobufjs/minimal";
3+
import Long = require("long");
4+
5+
export const protobufPackage = "foo";
6+
7+
export interface FieldOption {
8+
normalField: bigint;
9+
numberField: number;
10+
stringField: string;
11+
}
12+
13+
function createBaseFieldOption(): FieldOption {
14+
return { normalField: BigInt("0"), numberField: 0, stringField: "0" };
15+
}
16+
17+
export const FieldOption = {
18+
encode(message: FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
19+
if (message.normalField !== BigInt("0")) {
20+
if (BigInt.asIntN(64, message.normalField) !== message.normalField) {
21+
throw new globalThis.Error("value provided for field message.normalField of type int64 too large");
22+
}
23+
writer.uint32(8).int64(message.normalField.toString());
24+
}
25+
if (message.numberField !== 0) {
26+
if (BigInt.asIntN(64, BigInt(message.numberField)) !== BigInt(message.numberField)) {
27+
throw new globalThis.Error("value provided for field BigInt(message.numberField) of type int64 too large");
28+
}
29+
writer.uint32(16).int64(BigInt(message.numberField).toString());
30+
}
31+
if (message.stringField !== "0") {
32+
if (BigInt.asIntN(64, BigInt(message.stringField)) !== BigInt(message.stringField)) {
33+
throw new globalThis.Error("value provided for field BigInt(message.stringField) of type int64 too large");
34+
}
35+
writer.uint32(24).int64(BigInt(message.stringField).toString());
36+
}
37+
return writer;
38+
},
39+
40+
decode(input: _m0.Reader | Uint8Array, length?: number): FieldOption {
41+
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
42+
let end = length === undefined ? reader.len : reader.pos + length;
43+
const message = createBaseFieldOption();
44+
while (reader.pos < end) {
45+
const tag = reader.uint32();
46+
switch (tag >>> 3) {
47+
case 1:
48+
if (tag !== 8) {
49+
break;
50+
}
51+
52+
message.normalField = longToBigint(reader.int64() as Long);
53+
continue;
54+
case 2:
55+
if (tag !== 16) {
56+
break;
57+
}
58+
59+
message.numberField = longToNumber(reader.int64() as Long);
60+
continue;
61+
case 3:
62+
if (tag !== 24) {
63+
break;
64+
}
65+
66+
message.stringField = longToString(reader.int64() as Long);
67+
continue;
68+
}
69+
if ((tag & 7) === 4 || tag === 0) {
70+
break;
71+
}
72+
reader.skipType(tag & 7);
73+
}
74+
return message;
75+
},
76+
77+
fromJSON(object: any): FieldOption {
78+
return {
79+
normalField: isSet(object.normalField) ? BigInt(object.normalField) : BigInt("0"),
80+
numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0,
81+
stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0",
82+
};
83+
},
84+
85+
toJSON(message: FieldOption): unknown {
86+
const obj: any = {};
87+
if (message.normalField !== BigInt("0")) {
88+
obj.normalField = message.normalField.toString();
89+
}
90+
if (message.numberField !== 0) {
91+
obj.numberField = globalThis.Number(message.numberField);
92+
}
93+
if (message.stringField !== "0") {
94+
obj.stringField = globalThis.String(message.stringField);
95+
}
96+
return obj;
97+
},
98+
99+
create<I extends Exact<DeepPartial<FieldOption>, I>>(base?: I): FieldOption {
100+
return FieldOption.fromPartial(base ?? ({} as any));
101+
},
102+
fromPartial<I extends Exact<DeepPartial<FieldOption>, I>>(object: I): FieldOption {
103+
const message = createBaseFieldOption();
104+
message.normalField = object.normalField ?? BigInt("0");
105+
message.numberField = object.numberField ?? 0;
106+
message.stringField = object.stringField ?? "0";
107+
return message;
108+
},
109+
};
110+
111+
type Builtin = Date | Function | Uint8Array | string | number | boolean | bigint | undefined;
112+
113+
export type DeepPartial<T> = T extends Builtin ? T
114+
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
115+
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
116+
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
117+
: Partial<T>;
118+
119+
type KeysOfUnion<T> = T extends T ? keyof T : never;
120+
export type Exact<P, I extends P> = P extends Builtin ? P
121+
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
122+
123+
function longToNumber(long: Long): number {
124+
if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) {
125+
throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
126+
}
127+
return long.toNumber();
128+
}
129+
130+
function longToString(long: Long) {
131+
return long.toString();
132+
}
133+
134+
function longToBigint(long: Long) {
135+
return BigInt(long.toString());
136+
}
137+
138+
if (_m0.util.Long !== Long) {
139+
_m0.util.Long = Long as any;
140+
_m0.configure();
141+
}
142+
143+
function isSet(value: any): boolean {
144+
return value !== null && value !== undefined;
145+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const base = require("../../jest.config");
2+
3+
// Set maxWorkers for assertion failures involving BigInt
4+
// https://github.com/jestjs/jest/issues/11617
5+
module.exports = {
6+
...base,
7+
maxWorkers: 1,
8+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
forceLong=bigint,useJsTypeOverride=true
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { FieldOption } from "./fieldoption-jstype-with-forcelong-long";
2+
import { hexToUint8Array, uint8ArrayToHex } from "../utils";
3+
// @ts-ignore
4+
import Long = require("long");
5+
6+
describe("FieldOption jstype with ForceLong long", () => {
7+
describe("encode", () => {
8+
it("should encode the message", () => {
9+
const message: FieldOption = {
10+
normalField: Long.fromValue(123),
11+
numberField: 456,
12+
stringField: "789",
13+
};
14+
15+
const writer = FieldOption.encode(message);
16+
const buffer = writer.finish();
17+
18+
expect(uint8ArrayToHex(buffer)).toEqual("087b10c803189506");
19+
});
20+
});
21+
22+
describe("decode", () => {
23+
it("should decode the message", () => {
24+
const buffer = hexToUint8Array("087b10c803189506");
25+
26+
const decodedMessage = FieldOption.decode(buffer);
27+
28+
expect(decodedMessage).toEqual({
29+
normalField: Long.fromValue(123),
30+
numberField: 456,
31+
stringField: "789",
32+
});
33+
});
34+
});
35+
36+
describe("fromJSON", () => {
37+
it("should create a message from JSON", () => {
38+
const json = {
39+
normalField: "123",
40+
numberField: "456",
41+
stringField: "789",
42+
};
43+
44+
const message = FieldOption.fromJSON(json);
45+
46+
expect(message).toEqual({
47+
normalField: Long.fromValue(123),
48+
numberField: 456,
49+
stringField: "789",
50+
});
51+
});
52+
});
53+
54+
describe("toJSON", () => {
55+
it("should convert the message to JSON", () => {
56+
const message: FieldOption = {
57+
normalField: Long.fromValue(123),
58+
numberField: 456,
59+
stringField: "789",
60+
};
61+
62+
const json = FieldOption.toJSON(message);
63+
64+
expect(json).toEqual({
65+
normalField: "123",
66+
numberField: 456,
67+
stringField: "789",
68+
});
69+
});
70+
});
71+
72+
describe("create", () => {
73+
it("should create a message with default values", () => {
74+
const message = FieldOption.create();
75+
76+
expect(message).toEqual({
77+
normalField: Long.fromValue(0),
78+
numberField: 0,
79+
stringField: "0",
80+
});
81+
});
82+
83+
it("should create a message with provided values", () => {
84+
const message = FieldOption.create({
85+
normalField: Long.fromValue(123),
86+
numberField: 456,
87+
stringField: "789",
88+
});
89+
90+
expect(message).toEqual({
91+
normalField: Long.fromValue(123),
92+
numberField: 456,
93+
stringField: "789",
94+
});
95+
});
96+
});
97+
98+
describe("fromPartial", () => {
99+
it("should create a message from a partial object", () => {
100+
const partial = FieldOption.fromPartial({
101+
normalField: Long.fromValue(123),
102+
stringField: "789",
103+
});
104+
105+
expect(partial).toEqual({
106+
normalField: Long.fromValue(123),
107+
numberField: 0,
108+
stringField: "789",
109+
});
110+
});
111+
});
112+
});

0 commit comments

Comments
 (0)