Skip to content

Support symbol properties on extension objects #4234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 14, 2024
Merged
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
8 changes: 8 additions & 0 deletions src/jsutils/ObjMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export interface ReadOnlyObjMap<T> {
readonly [key: string]: T;
}

export interface ReadOnlyObjMapWithSymbol<T> {
readonly [key: string | symbol]: T;
}

export type ReadOnlyObjMapLike<T> =
| ReadOnlyObjMap<T>
| { readonly [key: string]: T };

export type ReadOnlyObjMapSymbolLike<T> =
| ReadOnlyObjMapWithSymbol<T>
| { readonly [key: string | symbol]: T };
31 changes: 30 additions & 1 deletion src/jsutils/toObjMap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { Maybe } from './Maybe.js';
import type { ReadOnlyObjMap, ReadOnlyObjMapLike } from './ObjMap.js';
import type {
ReadOnlyObjMap,
ReadOnlyObjMapLike,
ReadOnlyObjMapSymbolLike,
ReadOnlyObjMapWithSymbol,
} from './ObjMap.js';

export function toObjMap<T>(
obj: Maybe<ReadOnlyObjMapLike<T>>,
Expand All @@ -16,5 +21,29 @@ export function toObjMap<T>(
for (const [key, value] of Object.entries(obj)) {
map[key] = value;
}

return map;
}

export function toObjMapWithSymbols<T>(
obj: Maybe<ReadOnlyObjMapSymbolLike<T>>,
): ReadOnlyObjMapWithSymbol<T> {
if (obj == null) {
return Object.create(null);
}

if (Object.getPrototypeOf(obj) === null) {
return obj;
}

const map = Object.create(null);
for (const [key, value] of Object.entries(obj)) {
map[key] = value;
}

for (const key of Object.getOwnPropertySymbols(obj)) {
map[key] = obj[key];
}

return map;
}
19 changes: 19 additions & 0 deletions src/type/__tests__/definition-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,25 @@ describe('Type System: Scalars', () => {
expect(someScalar.toConfig()).to.deep.equal(someScalarConfig);
});

it('supports symbol extensions', () => {
const test = Symbol.for('test');
const someScalarConfig: GraphQLScalarTypeConfig<unknown, unknown> = {
name: 'SomeScalar',
description: 'SomeScalar description.',
specifiedByURL: 'https://example.com/foo_spec',
serialize: passThroughFunc,
parseValue: passThroughFunc,
parseLiteral: passThroughFunc,
coerceInputLiteral: passThroughFunc,
valueToLiteral: passThroughFunc,
extensions: { [test]: 'extension' },
astNode: dummyAny,
extensionASTNodes: [dummyAny],
};
const someScalar = new GraphQLScalarType(someScalarConfig);
expect(someScalar.toConfig()).to.deep.equal(someScalarConfig);
});

it('provides default methods if omitted', () => {
const scalar = new GraphQLScalarType({ name: 'Foo' });

Expand Down
42 changes: 21 additions & 21 deletions src/type/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { ObjMap } from '../jsutils/ObjMap.js';
import type { Path } from '../jsutils/Path.js';
import type { PromiseOrValue } from '../jsutils/PromiseOrValue.js';
import { suggestionList } from '../jsutils/suggestionList.js';
import { toObjMap } from '../jsutils/toObjMap.js';
import { toObjMapWithSymbols } from '../jsutils/toObjMap.js';

import { GraphQLError } from '../error/GraphQLError.js';

Expand Down Expand Up @@ -511,7 +511,7 @@ export function resolveObjMapThunk<T>(thunk: ThunkObjMap<T>): ObjMap<T> {
* an object which can contain all the values you need.
*/
export interface GraphQLScalarTypeExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

/**
Expand Down Expand Up @@ -610,7 +610,7 @@ export class GraphQLScalarType<TInternal = unknown, TExternal = TInternal> {
((node, variables) => parseValue(valueFromASTUntyped(node, variables)));
this.coerceInputLiteral = config.coerceInputLiteral;
this.valueToLiteral = config.valueToLiteral;
this.extensions = toObjMap(config.extensions);
this.extensions = toObjMapWithSymbols(config.extensions);
this.astNode = config.astNode;
this.extensionASTNodes = config.extensionASTNodes ?? [];

Expand Down Expand Up @@ -725,7 +725,7 @@ interface GraphQLScalarTypeNormalizedConfig<TInternal, TExternal>
* you may find them useful.
*/
export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

/**
Expand Down Expand Up @@ -783,7 +783,7 @@ export class GraphQLObjectType<TSource = any, TContext = any> {
this.name = assertName(config.name);
this.description = config.description;
this.isTypeOf = config.isTypeOf;
this.extensions = toObjMap(config.extensions);
this.extensions = toObjMapWithSymbols(config.extensions);
this.astNode = config.astNode;
this.extensionASTNodes = config.extensionASTNodes ?? [];
this._fields = (defineFieldMap<TSource, TContext>).bind(
Expand Down Expand Up @@ -854,7 +854,7 @@ function defineFieldMap<TSource, TContext>(
resolve: fieldConfig.resolve,
subscribe: fieldConfig.subscribe,
deprecationReason: fieldConfig.deprecationReason,
extensions: toObjMap(fieldConfig.extensions),
extensions: toObjMapWithSymbols(fieldConfig.extensions),
astNode: fieldConfig.astNode,
};
});
Expand All @@ -869,7 +869,7 @@ export function defineArguments(
type: argConfig.type,
defaultValue: defineDefaultValue(argName, argConfig),
deprecationReason: argConfig.deprecationReason,
extensions: toObjMap(argConfig.extensions),
extensions: toObjMapWithSymbols(argConfig.extensions),
astNode: argConfig.astNode,
}));
}
Expand Down Expand Up @@ -980,7 +980,7 @@ export interface GraphQLResolveInfo {
* you may find them useful.
*/
export interface GraphQLFieldExtensions<_TSource, _TContext, _TArgs = any> {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

export interface GraphQLFieldConfig<TSource, TContext, TArgs = any> {
Expand Down Expand Up @@ -1008,7 +1008,7 @@ export type GraphQLFieldConfigArgumentMap = ObjMap<GraphQLArgumentConfig>;
* an object which can contain all the values you need.
*/
export interface GraphQLArgumentExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

export interface GraphQLArgumentConfig {
Expand Down Expand Up @@ -1085,7 +1085,7 @@ export function defineDefaultValue(
* an object which can contain all the values you need.
*/
export interface GraphQLInterfaceTypeExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

/**
Expand Down Expand Up @@ -1122,7 +1122,7 @@ export class GraphQLInterfaceType<TSource = any, TContext = any> {
this.name = assertName(config.name);
this.description = config.description;
this.resolveType = config.resolveType;
this.extensions = toObjMap(config.extensions);
this.extensions = toObjMapWithSymbols(config.extensions);
this.astNode = config.astNode;
this.extensionASTNodes = config.extensionASTNodes ?? [];
this._fields = (defineFieldMap<TSource, TContext>).bind(
Expand Down Expand Up @@ -1206,7 +1206,7 @@ interface GraphQLInterfaceTypeNormalizedConfig<TSource, TContext>
* an object which can contain all the values you need.
*/
export interface GraphQLUnionTypeExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

/**
Expand Down Expand Up @@ -1247,7 +1247,7 @@ export class GraphQLUnionType {
this.name = assertName(config.name);
this.description = config.description;
this.resolveType = config.resolveType;
this.extensions = toObjMap(config.extensions);
this.extensions = toObjMapWithSymbols(config.extensions);
this.astNode = config.astNode;
this.extensionASTNodes = config.extensionASTNodes ?? [];

Expand Down Expand Up @@ -1324,7 +1324,7 @@ interface GraphQLUnionTypeNormalizedConfig
* an object which can contain all the values you need.
*/
export interface GraphQLEnumTypeExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

function enumValuesFromConfig(values: GraphQLEnumValueConfigMap) {
Expand All @@ -1333,7 +1333,7 @@ function enumValuesFromConfig(values: GraphQLEnumValueConfigMap) {
description: valueConfig.description,
value: valueConfig.value !== undefined ? valueConfig.value : valueName,
deprecationReason: valueConfig.deprecationReason,
extensions: toObjMap(valueConfig.extensions),
extensions: toObjMapWithSymbols(valueConfig.extensions),
astNode: valueConfig.astNode,
}));
}
Expand Down Expand Up @@ -1378,7 +1378,7 @@ export class GraphQLEnumType /* <T> */ {
constructor(config: Readonly<GraphQLEnumTypeConfig /* <T> */>) {
this.name = assertName(config.name);
this.description = config.description;
this.extensions = toObjMap(config.extensions);
this.extensions = toObjMapWithSymbols(config.extensions);
this.astNode = config.astNode;
this.extensionASTNodes = config.extensionASTNodes ?? [];

Expand Down Expand Up @@ -1559,7 +1559,7 @@ export type GraphQLEnumValueConfigMap /* <T> */ =
* an object which can contain all the values you need.
*/
export interface GraphQLEnumValueExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

export interface GraphQLEnumValueConfig {
Expand Down Expand Up @@ -1589,7 +1589,7 @@ export interface GraphQLEnumValue {
* an object which can contain all the values you need.
*/
export interface GraphQLInputObjectTypeExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

/**
Expand Down Expand Up @@ -1626,7 +1626,7 @@ export class GraphQLInputObjectType {
constructor(config: Readonly<GraphQLInputObjectTypeConfig>) {
this.name = assertName(config.name);
this.description = config.description;
this.extensions = toObjMap(config.extensions);
this.extensions = toObjMapWithSymbols(config.extensions);
this.astNode = config.astNode;
this.extensionASTNodes = config.extensionASTNodes ?? [];
this.isOneOf = config.isOneOf ?? false;
Expand Down Expand Up @@ -1686,7 +1686,7 @@ function defineInputFieldMap(
type: fieldConfig.type,
defaultValue: defineDefaultValue(fieldName, fieldConfig),
deprecationReason: fieldConfig.deprecationReason,
extensions: toObjMap(fieldConfig.extensions),
extensions: toObjMapWithSymbols(fieldConfig.extensions),
astNode: fieldConfig.astNode,
}));
}
Expand Down Expand Up @@ -1718,7 +1718,7 @@ interface GraphQLInputObjectTypeNormalizedConfig
* an object which can contain all the values you need.
*/
export interface GraphQLInputFieldExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

export interface GraphQLInputFieldConfig {
Expand Down
6 changes: 3 additions & 3 deletions src/type/directives.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { inspect } from '../jsutils/inspect.js';
import { instanceOf } from '../jsutils/instanceOf.js';
import type { Maybe } from '../jsutils/Maybe.js';
import { toObjMap } from '../jsutils/toObjMap.js';
import { toObjMapWithSymbols } from '../jsutils/toObjMap.js';

import type { DirectiveDefinitionNode } from '../language/ast.js';
import { DirectiveLocation } from '../language/directiveLocation.js';
Expand Down Expand Up @@ -44,7 +44,7 @@ export function assertDirective(directive: unknown): GraphQLDirective {
* an object which can contain all the values you need.
*/
export interface GraphQLDirectiveExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

/**
Expand All @@ -65,7 +65,7 @@ export class GraphQLDirective {
this.description = config.description;
this.locations = config.locations;
this.isRepeatable = config.isRepeatable ?? false;
this.extensions = toObjMap(config.extensions);
this.extensions = toObjMapWithSymbols(config.extensions);
this.astNode = config.astNode;

const args = config.args ?? {};
Expand Down
6 changes: 3 additions & 3 deletions src/type/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { inspect } from '../jsutils/inspect.js';
import { instanceOf } from '../jsutils/instanceOf.js';
import type { Maybe } from '../jsutils/Maybe.js';
import type { ObjMap } from '../jsutils/ObjMap.js';
import { toObjMap } from '../jsutils/toObjMap.js';
import { toObjMapWithSymbols } from '../jsutils/toObjMap.js';

import type { GraphQLError } from '../error/GraphQLError.js';

Expand Down Expand Up @@ -61,7 +61,7 @@ export function assertSchema(schema: unknown): GraphQLSchema {
* an object which can contain all the values you need.
*/
export interface GraphQLSchemaExtensions {
[attributeName: string]: unknown;
[attributeName: string | symbol]: unknown;
}

/**
Expand Down Expand Up @@ -162,7 +162,7 @@ export class GraphQLSchema {
this.__validationErrors = config.assumeValid === true ? [] : undefined;

this.description = config.description;
this.extensions = toObjMap(config.extensions);
this.extensions = toObjMapWithSymbols(config.extensions);
this.astNode = config.astNode;
this.extensionASTNodes = config.extensionASTNodes ?? [];

Expand Down
Loading