Skip to content

Commit f6036a6

Browse files
committed
streamline scalar coercion (#4549)
just refactoring, adds additional test for coverage, no changes
1 parent 5e147b1 commit f6036a6

File tree

2 files changed

+128
-56
lines changed

2 files changed

+128
-56
lines changed

src/type/__tests__/scalars-test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ describe('Type System: Specified scalar types', () => {
151151
expect(() => coerceOutputValue(-9876504321)).to.throw(
152152
'Int cannot represent non 32-bit signed integer value: -9876504321',
153153
);
154+
expect(() => coerceOutputValue('9876504321')).to.throw(
155+
'Int cannot represent non 32-bit signed integer value: "9876504321"',
156+
);
157+
expect(() => coerceOutputValue('-9876504321')).to.throw(
158+
'Int cannot represent non 32-bit signed integer value: "-9876504321"',
159+
);
154160

155161
// Too big to represent as an Int in JavaScript or GraphQL
156162
expect(() => coerceOutputValue(1e100)).to.throw(

src/type/scalars.ts

Lines changed: 122 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -31,41 +31,27 @@ export const GraphQLInt = new GraphQLScalarType<number>({
3131
coerceOutputValue(outputValue) {
3232
const coercedValue = coerceOutputValueObject(outputValue);
3333

34+
if (typeof coercedValue === 'number') {
35+
return coerceIntFromNumber(coercedValue);
36+
}
3437
if (typeof coercedValue === 'boolean') {
3538
return coercedValue ? 1 : 0;
3639
}
37-
38-
let num = coercedValue;
39-
if (typeof coercedValue === 'string' && coercedValue !== '') {
40-
num = Number(coercedValue);
41-
}
42-
43-
if (typeof num !== 'number' || !Number.isInteger(num)) {
44-
throw new GraphQLError(
45-
`Int cannot represent non-integer value: ${inspect(coercedValue)}`,
46-
);
47-
}
48-
if (num > GRAPHQL_MAX_INT || num < GRAPHQL_MIN_INT) {
49-
throw new GraphQLError(
50-
'Int cannot represent non 32-bit signed integer value: ' +
51-
inspect(coercedValue),
52-
);
40+
if (typeof coercedValue === 'string') {
41+
return coerceIntFromString(coercedValue);
5342
}
54-
return num;
43+
throw new GraphQLError(
44+
`Int cannot represent non-integer value: ${inspect(coercedValue)}`,
45+
);
5546
},
5647

5748
coerceInputValue(inputValue) {
58-
if (typeof inputValue !== 'number' || !Number.isInteger(inputValue)) {
59-
throw new GraphQLError(
60-
`Int cannot represent non-integer value: ${inspect(inputValue)}`,
61-
);
49+
if (typeof inputValue === 'number') {
50+
return coerceIntFromNumber(inputValue);
6251
}
63-
if (inputValue > GRAPHQL_MAX_INT || inputValue < GRAPHQL_MIN_INT) {
64-
throw new GraphQLError(
65-
`Int cannot represent non 32-bit signed integer value: ${inputValue}`,
66-
);
67-
}
68-
return inputValue;
52+
throw new GraphQLError(
53+
`Int cannot represent non-integer value: ${inspect(inputValue)}`,
54+
);
6955
},
7056

7157
coerceInputLiteral(valueNode) {
@@ -104,30 +90,27 @@ export const GraphQLFloat = new GraphQLScalarType<number>({
10490
coerceOutputValue(outputValue) {
10591
const coercedValue = coerceOutputValueObject(outputValue);
10692

93+
if (typeof coercedValue === 'number') {
94+
return coerceFloatFromNumber(coercedValue);
95+
}
10796
if (typeof coercedValue === 'boolean') {
10897
return coercedValue ? 1 : 0;
10998
}
110-
111-
let num = coercedValue;
112-
if (typeof coercedValue === 'string' && coercedValue !== '') {
113-
num = Number(coercedValue);
114-
}
115-
116-
if (typeof num !== 'number' || !Number.isFinite(num)) {
117-
throw new GraphQLError(
118-
`Float cannot represent non numeric value: ${inspect(coercedValue)}`,
119-
);
99+
if (typeof coercedValue === 'string') {
100+
return coerceFloatFromString(coercedValue);
120101
}
121-
return num;
102+
throw new GraphQLError(
103+
`Float cannot represent non numeric value: ${inspect(coercedValue)}`,
104+
);
122105
},
123106

124107
coerceInputValue(inputValue) {
125-
if (typeof inputValue !== 'number' || !Number.isFinite(inputValue)) {
126-
throw new GraphQLError(
127-
`Float cannot represent non numeric value: ${inspect(inputValue)}`,
128-
);
108+
if (typeof inputValue === 'number') {
109+
return coerceFloatFromNumber(inputValue);
129110
}
130-
return inputValue;
111+
throw new GraphQLError(
112+
`Float cannot represent non numeric value: ${inspect(inputValue)}`,
113+
);
131114
},
132115

133116
coerceInputLiteral(valueNode) {
@@ -163,8 +146,8 @@ export const GraphQLString = new GraphQLScalarType<string>({
163146
if (typeof coercedValue === 'boolean') {
164147
return coercedValue ? 'true' : 'false';
165148
}
166-
if (typeof coercedValue === 'number' && Number.isFinite(coercedValue)) {
167-
return coercedValue.toString();
149+
if (typeof coercedValue === 'number') {
150+
return coerceStringFromNumber(coercedValue);
168151
}
169152
throw new GraphQLError(
170153
`String cannot represent value: ${inspect(outputValue)}`,
@@ -207,8 +190,8 @@ export const GraphQLBoolean = new GraphQLScalarType<boolean>({
207190
if (typeof coercedValue === 'boolean') {
208191
return coercedValue;
209192
}
210-
if (Number.isFinite(coercedValue)) {
211-
return coercedValue !== 0;
193+
if (typeof coercedValue === 'number') {
194+
return coerceBooleanFromNumber(coercedValue);
212195
}
213196
throw new GraphQLError(
214197
`Boolean cannot represent a non boolean value: ${inspect(coercedValue)}`,
@@ -252,8 +235,8 @@ export const GraphQLID = new GraphQLScalarType<string>({
252235
if (typeof coercedValue === 'string') {
253236
return coercedValue;
254237
}
255-
if (Number.isInteger(coercedValue)) {
256-
return String(coercedValue);
238+
if (typeof coercedValue === 'number') {
239+
return coerceIDFromNumber(coercedValue);
257240
}
258241
throw new GraphQLError(
259242
`ID cannot represent value: ${inspect(outputValue)}`,
@@ -264,8 +247,8 @@ export const GraphQLID = new GraphQLScalarType<string>({
264247
if (typeof inputValue === 'string') {
265248
return inputValue;
266249
}
267-
if (typeof inputValue === 'number' && Number.isInteger(inputValue)) {
268-
return inputValue.toString();
250+
if (typeof inputValue === 'number') {
251+
return coerceIDFromNumber(inputValue);
269252
}
270253
throw new GraphQLError(`ID cannot represent value: ${inspect(inputValue)}`);
271254
},
@@ -282,12 +265,14 @@ export const GraphQLID = new GraphQLScalarType<string>({
282265
},
283266
valueToLiteral(value) {
284267
// ID types can use number values and Int literals.
285-
const stringValue = Number.isInteger(value) ? String(value) : value;
286-
if (typeof stringValue === 'string') {
268+
if (typeof value === 'string') {
287269
// Will parse as an IntValue.
288-
return /^-?(?:0|[1-9][0-9]*)$/.test(stringValue)
289-
? { kind: Kind.INT, value: stringValue }
290-
: { kind: Kind.STRING, value: stringValue, block: false };
270+
return /^-?(?:0|[1-9][0-9]*)$/.test(value)
271+
? { kind: Kind.INT, value }
272+
: { kind: Kind.STRING, value, block: false };
273+
}
274+
if (typeof value === 'number') {
275+
return { kind: Kind.INT, value: coerceIDFromNumber(value) };
291276
}
292277
},
293278
});
@@ -322,3 +307,84 @@ function coerceOutputValueObject(outputValue: unknown): unknown {
322307
}
323308
return outputValue;
324309
}
310+
311+
function coerceIntFromNumber(value: number): number {
312+
if (!Number.isInteger(value)) {
313+
throw new GraphQLError(
314+
`Int cannot represent non-integer value: ${inspect(value)}`,
315+
);
316+
}
317+
if (value > GRAPHQL_MAX_INT || value < GRAPHQL_MIN_INT) {
318+
throw new GraphQLError(
319+
`Int cannot represent non 32-bit signed integer value: ${inspect(value)}`,
320+
);
321+
}
322+
return value;
323+
}
324+
325+
function coerceIntFromString(value: string): number {
326+
if (value === '') {
327+
throw new GraphQLError(
328+
`Int cannot represent non-integer value: ${inspect(value)}`,
329+
);
330+
}
331+
const num = Number(value);
332+
if (!Number.isInteger(num)) {
333+
throw new GraphQLError(
334+
`Int cannot represent non-integer value: ${inspect(value)}`,
335+
);
336+
}
337+
if (num > GRAPHQL_MAX_INT || num < GRAPHQL_MIN_INT) {
338+
throw new GraphQLError(
339+
`Int cannot represent non 32-bit signed integer value: ${inspect(value)}`,
340+
);
341+
}
342+
return num;
343+
}
344+
345+
function coerceFloatFromNumber(value: number): number {
346+
if (!Number.isFinite(value)) {
347+
throw new GraphQLError(
348+
`Float cannot represent non numeric value: ${inspect(value)}`,
349+
);
350+
}
351+
return value;
352+
}
353+
354+
function coerceFloatFromString(value: string): number {
355+
if (value === '') {
356+
throw new GraphQLError(
357+
`Float cannot represent non numeric value: ${inspect(value)}`,
358+
);
359+
}
360+
const num = Number(value);
361+
if (!Number.isFinite(num)) {
362+
throw new GraphQLError(
363+
`Float cannot represent non numeric value: ${inspect(value)}`,
364+
);
365+
}
366+
return num;
367+
}
368+
369+
function coerceStringFromNumber(value: number): string {
370+
if (!Number.isFinite(value)) {
371+
throw new GraphQLError(`String cannot represent value: ${inspect(value)}`);
372+
}
373+
return String(value);
374+
}
375+
376+
function coerceBooleanFromNumber(value: number): boolean {
377+
if (!Number.isFinite(value)) {
378+
throw new GraphQLError(
379+
`Boolean cannot represent a non boolean value: ${inspect(value)}`,
380+
);
381+
}
382+
return value !== 0;
383+
}
384+
385+
function coerceIDFromNumber(value: number): string {
386+
if (!Number.isInteger(value)) {
387+
throw new GraphQLError(`ID cannot represent value: ${inspect(value)}`);
388+
}
389+
return String(value);
390+
}

0 commit comments

Comments
 (0)