Skip to content

Commit 9284dab

Browse files
ardatanyaacovCR
authored andcommitted
JavaScript BigInt support
1 parent 0ea9241 commit 9284dab

File tree

5 files changed

+64
-16
lines changed

5 files changed

+64
-16
lines changed

src/jsutils/isInteger.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function isInteger(value: unknown): value is number | bigint {
2+
const valueTypeOf = typeof value;
3+
if (valueTypeOf === 'number') {
4+
return Number.isInteger(value);
5+
}
6+
return valueTypeOf === 'bigint';
7+
}

src/jsutils/isNumeric.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function isNumeric(value: unknown): value is number | bigint {
2+
const valueTypeOf = typeof value;
3+
if (valueTypeOf === 'number') {
4+
return Number.isFinite(value);
5+
}
6+
return valueTypeOf === 'bigint';
7+
}

src/type/scalars.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { inspect } from '../jsutils/inspect.js';
2+
import { isInteger } from '../jsutils/isInteger.js';
3+
import { isNumeric } from '../jsutils/isNumeric.js';
24
import { isObjectLike } from '../jsutils/isObjectLike.js';
35

46
import { GraphQLError } from '../error/GraphQLError.js';
@@ -40,7 +42,7 @@ export const GraphQLInt = new GraphQLScalarType<number>({
4042
num = Number(coercedValue);
4143
}
4244

43-
if (typeof num !== 'number' || !Number.isInteger(num)) {
45+
if (!isInteger(num)) {
4446
throw new GraphQLError(
4547
`Int cannot represent non-integer value: ${inspect(coercedValue)}`,
4648
);
@@ -51,21 +53,22 @@ export const GraphQLInt = new GraphQLScalarType<number>({
5153
inspect(coercedValue),
5254
);
5355
}
54-
return num;
56+
return Number(num);
5557
},
5658

5759
parseValue(inputValue) {
58-
if (typeof inputValue !== 'number' || !Number.isInteger(inputValue)) {
60+
if (!isInteger(inputValue)) {
5961
throw new GraphQLError(
6062
`Int cannot represent non-integer value: ${inspect(inputValue)}`,
6163
);
6264
}
63-
if (inputValue > GRAPHQL_MAX_INT || inputValue < GRAPHQL_MIN_INT) {
65+
const coercedVal = Number(inputValue);
66+
if (coercedVal > GRAPHQL_MAX_INT || coercedVal < GRAPHQL_MIN_INT) {
6467
throw new GraphQLError(
6568
`Int cannot represent non 32-bit signed integer value: ${inputValue}`,
6669
);
6770
}
68-
return inputValue;
71+
return coercedVal;
6972
},
7073

7174
parseConstLiteral(valueNode) {
@@ -96,7 +99,7 @@ export const GraphQLInt = new GraphQLScalarType<number>({
9699
},
97100
});
98101

99-
export const GraphQLFloat = new GraphQLScalarType<number>({
102+
export const GraphQLFloat = new GraphQLScalarType<number | bigint>({
100103
name: 'Float',
101104
description:
102105
'The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).',
@@ -113,16 +116,17 @@ export const GraphQLFloat = new GraphQLScalarType<number>({
113116
num = Number(coercedValue);
114117
}
115118

116-
if (typeof num !== 'number' || !Number.isFinite(num)) {
119+
if (!isNumeric(num)) {
117120
throw new GraphQLError(
118121
`Float cannot represent non numeric value: ${inspect(coercedValue)}`,
119122
);
120123
}
124+
121125
return num;
122126
},
123127

124128
parseValue(inputValue) {
125-
if (typeof inputValue !== 'number' || !Number.isFinite(inputValue)) {
129+
if (!isNumeric(inputValue)) {
126130
throw new GraphQLError(
127131
`Float cannot represent non numeric value: ${inspect(inputValue)}`,
128132
);
@@ -163,8 +167,8 @@ export const GraphQLString = new GraphQLScalarType<string>({
163167
if (typeof coercedValue === 'boolean') {
164168
return coercedValue ? 'true' : 'false';
165169
}
166-
if (typeof coercedValue === 'number' && Number.isFinite(coercedValue)) {
167-
return coercedValue.toString();
170+
if (isNumeric(coercedValue)) {
171+
return String(coercedValue);
168172
}
169173
throw new GraphQLError(
170174
`String cannot represent value: ${inspect(outputValue)}`,
@@ -207,8 +211,8 @@ export const GraphQLBoolean = new GraphQLScalarType<boolean>({
207211
if (typeof coercedValue === 'boolean') {
208212
return coercedValue;
209213
}
210-
if (Number.isFinite(coercedValue)) {
211-
return coercedValue !== 0;
214+
if (isNumeric(coercedValue)) {
215+
return Number(coercedValue) !== 0;
212216
}
213217
throw new GraphQLError(
214218
`Boolean cannot represent a non boolean value: ${inspect(coercedValue)}`,
@@ -252,7 +256,7 @@ export const GraphQLID = new GraphQLScalarType<string>({
252256
if (typeof coercedValue === 'string') {
253257
return coercedValue;
254258
}
255-
if (Number.isInteger(coercedValue)) {
259+
if (isInteger(coercedValue)) {
256260
return String(coercedValue);
257261
}
258262
throw new GraphQLError(
@@ -264,8 +268,8 @@ export const GraphQLID = new GraphQLScalarType<string>({
264268
if (typeof inputValue === 'string') {
265269
return inputValue;
266270
}
267-
if (typeof inputValue === 'number' && Number.isInteger(inputValue)) {
268-
return inputValue.toString();
271+
if (isInteger(inputValue)) {
272+
return String(inputValue);
269273
}
270274
throw new GraphQLError(`ID cannot represent value: ${inspect(inputValue)}`);
271275
},

src/utilities/__tests__/astFromValue-test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,16 @@ describe('astFromValue', () => {
4141
value: false,
4242
});
4343

44-
expect(astFromValue(1, GraphQLBoolean)).to.deep.equal({
44+
expect(astFromValue(1n, GraphQLBoolean)).to.deep.equal({
4545
kind: 'BooleanValue',
4646
value: true,
4747
});
4848

49+
expect(astFromValue(0n, GraphQLBoolean)).to.deep.equal({
50+
kind: 'BooleanValue',
51+
value: false,
52+
});
53+
4954
const NonNullBoolean = new GraphQLNonNull(GraphQLBoolean);
5055
expect(astFromValue(0, NonNullBoolean)).to.deep.equal({
5156
kind: 'BooleanValue',
@@ -69,6 +74,11 @@ describe('astFromValue', () => {
6974
value: '10000',
7075
});
7176

77+
expect(astFromValue(1n, GraphQLInt)).to.deep.equal({
78+
kind: 'IntValue',
79+
value: '1',
80+
});
81+
7282
// GraphQL spec does not allow coercing non-integer values to Int to avoid
7383
// accidental data loss.
7484
expect(() => astFromValue(123.5, GraphQLInt)).to.throw(
@@ -96,6 +106,11 @@ describe('astFromValue', () => {
96106
value: '123',
97107
});
98108

109+
expect(astFromValue(9007199254740993n, GraphQLFloat)).to.deep.equal({
110+
kind: 'IntValue',
111+
value: '9007199254740993',
112+
});
113+
99114
expect(astFromValue(123.5, GraphQLFloat)).to.deep.equal({
100115
kind: 'FloatValue',
101116
value: '123.5',
@@ -133,6 +148,11 @@ describe('astFromValue', () => {
133148
value: '123',
134149
});
135150

151+
expect(astFromValue(9007199254740993n, GraphQLString)).to.deep.equal({
152+
kind: 'StringValue',
153+
value: '9007199254740993',
154+
});
155+
136156
expect(astFromValue(false, GraphQLString)).to.deep.equal({
137157
kind: 'StringValue',
138158
value: 'false',
@@ -183,6 +203,11 @@ describe('astFromValue', () => {
183203
value: '01',
184204
});
185205

206+
expect(astFromValue(9007199254740993n, GraphQLID)).to.deep.equal({
207+
kind: 'IntValue',
208+
value: '9007199254740993',
209+
});
210+
186211
expect(() => astFromValue(false, GraphQLID)).to.throw(
187212
'ID cannot represent value: false',
188213
);

src/utilities/astFromValue.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ export function astFromValue(
118118
: { kind: Kind.FLOAT, value: stringNum };
119119
}
120120

121+
if (typeof serialized === 'bigint') {
122+
const stringNum = String(serialized);
123+
return { kind: Kind.INT, value: stringNum };
124+
}
125+
121126
if (typeof serialized === 'string') {
122127
// Enum types use Enum literals.
123128
if (isEnumType(type)) {

0 commit comments

Comments
 (0)