Repository: FHIR/sushi (ts)

Issue:
SUSHI converting Reference to CodeableReference in R4B Snapshot1 extensions
If you have an R4B Snapshot1 project (e.g., `fhirVersion: 4.3.0-snapshot1`) and add this definition:
```
Extension: MyExtension
* value[x] only Reference(Resource)
```
SUSHI will (incorrectly) create a differential with this element:
```json
{
  "id": "Extension.value[x]",
  "path": "Extension.value[x]",
  "type": [
   {
      "code": "CodeableReference",
      "targetProfile": [
        "http://hl7.org/fhir/StructureDefinition/Resource"
      ]
    }
  ]
}
```

If you first restrict to just `Reference` and _then_ to `Reference(Resource)`, it works correctly though.

`¯\_(ツ)_/¯`

See: https://chat.fhir.org/#narrow/stream/215610-shorthand/topic/References.20for.204B.20Snapshot.20.231/near/274743533

Pull request description:
Prefer constraining to Reference type
Fixes #1044.

When constraining an element type using the `Reference()` keyword, SUSHI will now prefer the `Reference` type over the `CodeableReference` type if both are allowed on the element.

If you try the simple example in the issue, you should see that the `Extension.value[x]` element now has the following type:

```
{
  "id": "Extension.value[x]",
  "path": "Extension.value[x]",
  "type": [
    {
      "code": "Reference",
      "targetProfile": [
        "http://hl7.org/fhir/StructureDefinition/Resource"
      ]
    }
  ]
}
```

Because the R4B Snapshot1 and R5 Extensions support both Reference and CodeableReference, SUSHI was previously finding the CodeableReference type first and just using that. It now looks for Reference first and only uses CodeableReference if it cannot find Reference.

Patch:
diff --git a/src/fhirtypes/ElementDefinition.ts b/src/fhirtypes/ElementDefinition.ts
index 5ae69b6..7472133 100644
--- a/src/fhirtypes/ElementDefinition.ts
+++ b/src/fhirtypes/ElementDefinition.ts
@@ -1083,11 +1083,20 @@ export class ElementDefinition {
         // References always have a code 'Reference' w/ the referenced type's defining URL set as
         // one of the targetProfiles.  If the targetProfile property is null, that means any
         // reference is allowed.
+        // When 'Reference' keyword is used, prefer to match on the 'Reference' type over the
+        // 'CodeableReference' type if they both exist on the element.
         matchedType = targetTypes.find(
           t2 =>
-            isReferenceType(t2.code) &&
+            t2.code === 'Reference' &&
             (t2.targetProfile == null || t2.targetProfile.includes(md.url))
         );
+        if (!matchedType) {
+          matchedType = targetTypes.find(
+            t2 =>
+              t2.code === 'CodeableReference' &&
+              (t2.targetProfile == null || t2.targetProfile.includes(md.url))
+          );
+        }
       } else if (type.isCanonical) {
         // Canonicals always have a code 'canonical' w/ the referenced type's defining URL set as
         // one of the targetProfiles.  If the targetProfile property is null, that means any


Test patch:
diff --git a/test/fhirtypes/ElementDefinition.constrainType.test.ts b/test/fhirtypes/ElementDefinition.constrainType.test.ts
index 0b19752..d5ffaae 100644
--- a/test/fhirtypes/ElementDefinition.constrainType.test.ts
+++ b/test/fhirtypes/ElementDefinition.constrainType.test.ts
@@ -910,6 +910,22 @@ describe('ElementDefinition R5', () => {
       );
     });
 
+    it('should constrain an element to Reference type with the Reference() keyword when both Reference and CodeableReference is allowed', () => {
+      // R5 Extension allows both Reference and CodeableReference types
+      const r5Extension = fisher.fishForStructureDefinition('Extension');
+      const valueX = r5Extension.elements.find(e => e.id === 'Extension.value[x]');
+      // * value[x] only Reference(Resource)
+      const onlyRule = new OnlyRule('value[x]');
+      onlyRule.types = [{ type: 'Resource', isReference: true }];
+      valueX.constrainType(onlyRule, fisher);
+      expect(valueX.type).toHaveLength(1);
+      expect(valueX.type[0]).toEqual(
+        new ElementDefinitionType('Reference').withTargetProfiles(
+          'http://hl7.org/fhir/StructureDefinition/Resource'
+        )
+      );
+    });
+
     it('should throw InvalidTypeError when a passed in reference to a type that cannot constrain any existing references to types on a CodeableReference', () => {
       const addresses = r5CarePlan.elements.find(e => e.id === 'CarePlan.addresses');
       const onlyRule = new OnlyRule('addresses');
diff --git a/test/testhelpers/testdefs/r5-definitions/package/StructureDefinition-Extension.json b/test/testhelpers/testdefs/r5-definitions/package/StructureDefinition-Extension.json
index af0e4d5..1cd59e6 100644
--- a/test/testhelpers/testdefs/r5-definitions/package/StructureDefinition-Extension.json
+++ b/test/testhelpers/testdefs/r5-definitions/package/StructureDefinition-Extension.json
@@ -276,6 +276,9 @@
       {
         "code" : "CodeableConcept"
       },
+      {
+        "code" : "CodeableReference"
+      },
       {
         "code" : "Coding"
       },
@@ -502,6 +505,9 @@
       {
         "code" : "CodeableConcept"
       },
+      {
+        "code" : "CodeableReference"
+      },
       {
         "code" : "Coding"
       },