in composition/src/v1/normalization/normalization-factory.ts [2056:2170]
validateInterfaceImplementations(data: CompositeOutputData) {
if (data.implementedInterfaceTypeNames.size < 1) {
return;
}
const isParentInaccessible = data.directivesByDirectiveName.has(INACCESSIBLE);
const implementationErrorsMap = new Map<string, ImplementationErrors>();
const invalidImplementationTypeStringByTypeName = new Map<string, string>();
let doesInterfaceImplementItself = false;
for (const interfaceTypeName of data.implementedInterfaceTypeNames) {
const interfaceData = this.parentDefinitionDataByTypeName.get(interfaceTypeName);
// This check is so undefined type errors are not improperly propagated
if (BASE_SCALARS.has(interfaceTypeName)) {
this.referencedTypeNames.add(interfaceTypeName);
}
if (!interfaceData) {
continue;
}
if (interfaceData.kind !== Kind.INTERFACE_TYPE_DEFINITION) {
invalidImplementationTypeStringByTypeName.set(interfaceData.name, kindToTypeString(interfaceData.kind));
continue;
}
if (data.name === interfaceData.name) {
doesInterfaceImplementItself = true;
continue;
}
const implementationErrors: ImplementationErrors = {
invalidFieldImplementations: new Map<string, InvalidFieldImplementation>(),
unimplementedFields: [],
};
let hasErrors = false;
for (const [fieldName, interfaceField] of interfaceData.fieldDataByFieldName) {
this.unvalidatedExternalFieldCoords.delete(`${data.name}.${fieldName}`);
let hasNestedErrors = false;
const fieldData = data.fieldDataByFieldName.get(fieldName);
if (!fieldData) {
hasErrors = true;
implementationErrors.unimplementedFields.push(fieldName);
continue;
}
const invalidFieldImplementation: InvalidFieldImplementation = {
invalidAdditionalArguments: new Set<string>(),
invalidImplementedArguments: [],
isInaccessible: false,
originalResponseType: printTypeNode(interfaceField.node.type),
unimplementedArguments: new Set<string>(),
};
// The implemented field type must be equally or more restrictive than the original interface field type
if (
!isTypeValidImplementation(
interfaceField.node.type,
fieldData.node.type,
this.concreteTypeNamesByAbstractTypeName,
)
) {
hasErrors = true;
hasNestedErrors = true;
invalidFieldImplementation.implementedResponseType = printTypeNode(fieldData.node.type);
}
const handledArguments = new Set<string>();
for (const [argumentName, interfaceArgument] of interfaceField.argumentDataByArgumentName) {
handledArguments.add(argumentName);
const containerArgument = fieldData.argumentDataByArgumentName.get(argumentName);
// The type implementing the interface must include all arguments with no variation for that argument
if (!containerArgument) {
hasErrors = true;
hasNestedErrors = true;
invalidFieldImplementation.unimplementedArguments.add(argumentName);
continue;
}
// Implemented arguments should be the exact same type
const actualType = printTypeNode(containerArgument.type as TypeNode);
const expectedType = printTypeNode(interfaceArgument.type as TypeNode);
if (expectedType !== actualType) {
hasErrors = true;
hasNestedErrors = true;
invalidFieldImplementation.invalidImplementedArguments.push({ actualType, argumentName, expectedType });
}
}
// Additional arguments must be optional (nullable)
for (const [argumentName, argumentData] of fieldData.argumentDataByArgumentName) {
if (handledArguments.has(argumentName)) {
continue;
}
if (argumentData.type.kind !== Kind.NON_NULL_TYPE) {
continue;
}
hasErrors = true;
hasNestedErrors = true;
invalidFieldImplementation.invalidAdditionalArguments.add(argumentName);
}
if (!isParentInaccessible && fieldData.isInaccessible && !interfaceField.isInaccessible) {
hasErrors = true;
hasNestedErrors = true;
invalidFieldImplementation.isInaccessible = true;
}
if (hasNestedErrors) {
implementationErrors.invalidFieldImplementations.set(fieldName, invalidFieldImplementation);
}
}
if (hasErrors) {
implementationErrorsMap.set(interfaceTypeName, implementationErrors);
}
}
if (invalidImplementationTypeStringByTypeName.size > 0) {
this.errors.push(invalidImplementedTypeError(data.name, invalidImplementationTypeStringByTypeName));
}
if (doesInterfaceImplementItself) {
this.errors.push(selfImplementationError(data.name));
}
if (implementationErrorsMap.size > 0) {
this.errors.push(
invalidInterfaceImplementationError(data.name, kindToTypeString(data.kind), implementationErrorsMap),
);
}
}