Skip to content

Reference.reference type rejects valid FHIR reference forms (urn:uuid, absolute URLs, fragments) #141

@ryukzak

Description

@ryukzak

Problem

The generated Reference<T> type narrows reference to `${T}/${string}`:

// fhir-types/hl7-fhir-r4-core/Reference.ts
export interface Reference<T extends string = string> extends Element {
    reference?: `${T}/${string}`;
    // ...
}

This rejects other literal reference forms allowed by FHIR:

  • urn:uuid:<uuid> — transaction Bundle placeholder references (server resolves them on commit)
  • urn:oid:<oid> — OID-based references
  • http://example.org/fhir/Patient/123 — absolute URLs
  • #p1 — fragment references to contained resources

Repro

This is a very common transaction-bundle pattern and it fails under tsc --strict:

import { USCoreBloodPressureProfile } from "./fhir-types/hl7-fhir-us-core/profiles";
import { randomUUID } from "node:crypto";

const patientUrn = `urn:uuid:${randomUUID()}`;

const bp = USCoreBloodPressureProfile.create({
  status: "final",
  subject: { reference: patientUrn },   // ❌ TS2322: Type 'string' is not assignable to type `Patient/${string}`
});

The profile class's subject field is Reference<"Patient">, so reference is typed as `Patient/${string}` and urn:uuid:... is rejected.

The only current workaround is an inline cast:

subject: { reference: patientUrn as `Patient/${string}` }

...which is a technical lie (the string doesn't actually start with Patient/) and defeats the purpose of the template-literal typing.

Suggested fix

Widen reference to a union that covers all FHIR-valid forms:

reference?:
  | `${T}/${string}`         // relative
  | `http://${string}`       // absolute
  | `https://${string}`      // absolute
  | `urn:uuid:${string}`     // bundle placeholder
  | `urn:oid:${string}`      // oid
  | `#${string}`;            // fragment to contained

This keeps the nice Patient/<id> narrowing for the common case while accepting the other forms. Alternative: fall back to plain string with a JSDoc note, though that loses the typing benefit.

Context

Hit while writing the tutorial "US Core Profiles in TypeScript with @atomic-ehr/codegen" — the tutorial's CSV-to-transaction-Bundle flow uses urn:uuid placeholder references for cross-resource links, which is the canonical FHIR pattern. Working example that reproduces this: developer-experience/atomic-ehr-codegen-typescript-us-core-profiles in HealthSamurai/examples.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions