Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .changeset/input-output-codegen-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'graphql-scalars': major
---

Emit distinct input/output codegen types for scalars whose parsed and returned types differ

Scalars whose parsed **input** type is narrower than the accepted resolver-return **output** type
now declare `codegenScalarType` as `{ input, output }`, so graphql-code-generator emits the two
positions separately instead of collapsing them into one union:

- `DateTime` / `Date` / `Time` / `Timestamp` — `input` is `Date`; `output` accepts a `Date` or an
ISO string (plus a unix `number` for `DateTime`/`Timestamp`)
- `Byte` — `input` is `Buffer`; `output` accepts `Buffer | string`
- `URL` — `input` is `URL`; `output` accepts `URL | string`
- `BigInt` — `input` is `bigint | number`; `output` accepts `bigint | number | string`
- `Port` / `Latitude` / `Longitude` — `input` is `number`; `output` accepts `string | number`

(`DateTimeISO` and `Long` inherit their base configs.)

Requires a version of `@graphql-codegen/typescript` whose `visitor-plugin-common` understands the
object form of `codegenScalarType` (graphql-code-generator#10893). Older versions read the extension
as a single string and will not handle the object form.
19 changes: 19 additions & 0 deletions .changeset/tighten-scalar-codegen-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'graphql-scalars': minor
---

Tighten scalar TypeScript and codegen types (#2554)

Every scalar now declares precise `GraphQLScalarType<TParsed, TSerialized>` generics, and
`codegenScalarType` consistently carries the resolver-return (output) type, so generated resolver
types reflect what each scalar actually parses and accepts:

- Enum scalars (CountryCode, Currency, CountryName) emit a string-literal union instead of `string`.
- USCurrency emits `number` — resolvers both return and receive cents as a number — instead of
`string`.
- DateTime and BigInt widen to cover every value `serialize` accepts.
- Timestamp's jsonSchema type is corrected to `integer` to match its numeric wire value.

These are type-level changes only, with no change to runtime behavior. Consumers relying on the
previously looser types may see new — and correct — compile errors where a value was typed too
broadly before.
2 changes: 1 addition & 1 deletion src/scalars/BigInt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const GraphQLBigIntConfig: GraphQLScalarTypeConfig<
return bigint;
},
extensions: {
codegenScalarType: 'bigint',
codegenScalarType: { input: 'bigint | number', output: 'bigint | number | string' },
jsonSchema: {
type: 'integer',
format: 'int64',
Expand Down
55 changes: 27 additions & 28 deletions src/scalars/Byte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,34 +79,33 @@ function parseObject(ast: ObjectValueNode) {
});
}

export const GraphQLByteConfig: GraphQLScalarTypeConfig<Buffer | string | BufferJson, Buffer> =
/*#__PURE__*/ {
name: 'Byte',
description: 'The `Byte` scalar type represents byte value as a Buffer',
serialize: validate,
parseValue: validate,
parseLiteral(ast: ASTNode) {
switch (ast.kind) {
case Kind.STRING:
return validate(ast.value, ast);
case Kind.OBJECT:
return parseObject(ast);
default:
throw createGraphQLError(
`Can only parse base64 or hex encoded strings as Byte, but got a: ${ast.kind}`,
{
nodes: [ast],
},
);
}
},
extensions: {
codegenScalarType: 'Buffer | string',
jsonSchema: {
type: 'string',
format: 'byte',
},
export const GraphQLByteConfig: GraphQLScalarTypeConfig<Buffer, Buffer> = /*#__PURE__*/ {
name: 'Byte',
description: 'The `Byte` scalar type represents byte value as a Buffer',
serialize: validate,
parseValue: validate,
parseLiteral(ast: ASTNode) {
switch (ast.kind) {
case Kind.STRING:
return validate(ast.value, ast);
case Kind.OBJECT:
return parseObject(ast);
default:
throw createGraphQLError(
`Can only parse base64 or hex encoded strings as Byte, but got a: ${ast.kind}`,
{
nodes: [ast],
},
);
}
},
extensions: {
codegenScalarType: { input: 'Buffer', output: 'Buffer | string' },
jsonSchema: {
type: 'string',
format: 'byte',
},
};
},
};

export const GraphQLByte = /*#__PURE__*/ new GraphQLScalarType(GraphQLByteConfig);
Loading