diff --git a/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/Bundle.ts b/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/Bundle.ts index 3a9e0138..4683d070 100644 --- a/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/Bundle.ts +++ b/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/Bundle.ts @@ -12,12 +12,12 @@ export type { BackboneElement } from "../hl7-fhir-r4-core/BackboneElement"; export type { Identifier } from "../hl7-fhir-r4-core/Identifier"; export type { Signature } from "../hl7-fhir-r4-core/Signature"; -export interface BundleEntry extends BackboneElement { +export interface BundleEntry extends BackboneElement { fullUrl?: string; link?: BundleLink[]; request?: BundleEntryRequest; - resource?: T; - response?: BundleEntryResponse; + resource?: T1; + response?: BundleEntryResponse; search?: BundleEntrySearch; } @@ -49,10 +49,10 @@ export interface BundleLink extends BackboneElement { } // CanonicalURL: http://hl7.org/fhir/StructureDefinition/Bundle (pkg: hl7.fhir.r4.core#4.0.1) -export interface Bundle extends Resource { +export interface Bundle extends Resource { resourceType: "Bundle"; - entry?: BundleEntry[]; + entry?: BundleEntry[]; identifier?: Identifier; link?: BundleLink[]; signature?: Signature; diff --git a/examples/typescript-r4/resource.test.ts b/examples/typescript-r4/resource.test.ts index 10b655f2..e700d3e6 100644 --- a/examples/typescript-r4/resource.test.ts +++ b/examples/typescript-r4/resource.test.ts @@ -121,6 +121,47 @@ test("Bundle with resources", () => { expect(bundle).toMatchSnapshot(); }); +test("Bundle narrows entry resources without type predicates", () => { + // A bundle carrying only Patients and Observations + const patient = createPatient(); + assert(patient.id); + const observation = createObservation(patient.id); + const bundle: Bundle = { + resourceType: "Bundle", + type: "transaction", + entry: [ + { fullUrl: `urn:uuid:${patient.id}`, resource: patient }, + { fullUrl: `urn:uuid:${observation.id}`, resource: observation }, + ], + }; + + // TS 5.5+ infers the type predicate from the discriminated union — no explicit `r is Observation` needed + const observations: Observation[] = (bundle.entry ?? []) + .map((e) => e.resource) + .filter((r) => r?.resourceType === "Observation"); + + expect(observations).toHaveLength(1); + expect(observations[0]!.id).toBe("glucose-obs-1"); +}); + +test("Bundle entry type is BundleEntry", () => { + const patient = createPatient(); + const entry: BundleEntry = { fullUrl: `urn:uuid:${patient.id}`, resource: patient }; + // resource is narrowed to Patient, not Resource + expect(entry.resource?.resourceType).toBe("Patient"); +}); + +test("Bundle defaults to Bundle (backwards compatible)", () => { + const patient = createPatient(); + // No type param — entry.resource is Resource | undefined (original behaviour) + const bundle: Bundle = { + resourceType: "Bundle", + type: "collection", + entry: [{ fullUrl: `urn:uuid:${patient.id}`, resource: patient }], + }; + expect(bundle.entry).toHaveLength(1); +}); + test("Reference accepts all FHIR literal reference forms", () => { // Relative reference — still narrowed to the typed form const relative: Observation["subject"] = { reference: "Patient/123" }; diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts index 146c333d..2ddde60d 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts @@ -61,6 +61,13 @@ export class observation_vitalsignsProfile { return profile; } + static is (resource: unknown) : resource is Observation { + if (typeof resource !== "object" || resource === null) return false; + const r = resource as { resourceType?: string; meta?: { profile?: string[] } }; + if (r.resourceType !== "Observation") return false; + return (r.meta?.profile ?? []).includes(observation_vitalsignsProfile.canonicalUrl); + } + static apply (resource: Observation) : observation_vitalsignsProfile { ensureProfile(resource, observation_vitalsignsProfile.canonicalUrl); resource.category = ensureSliceDefaults( diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts index 6d734099..c64e80b7 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreEthnicityExtension.ts @@ -73,6 +73,11 @@ export class USCoreEthnicityExtensionProfile { return profile; } + static is (resource: unknown) : resource is Extension { + if (typeof resource !== "object" || resource === null) return false; + return (resource as { url?: string }).url === USCoreEthnicityExtensionProfile.canonicalUrl; + } + static apply (resource: Extension) : USCoreEthnicityExtensionProfile { resource.url = USCoreEthnicityExtensionProfile.canonicalUrl; Object.assign(resource, { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreIndividualSexExtension.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreIndividualSexExtension.ts index 51942c3e..189b1ebc 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreIndividualSexExtension.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreIndividualSexExtension.ts @@ -38,6 +38,11 @@ export class USCoreIndividualSexExtensionProfile { return profile; } + static is (resource: unknown) : resource is Extension { + if (typeof resource !== "object" || resource === null) return false; + return (resource as { url?: string }).url === USCoreIndividualSexExtensionProfile.canonicalUrl; + } + static apply (resource: Extension) : USCoreIndividualSexExtensionProfile { resource.url = USCoreIndividualSexExtensionProfile.canonicalUrl; Object.assign(resource, { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreInterpreterNeededExtension.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreInterpreterNeededExtension.ts index bcc0496e..f8ef0585 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreInterpreterNeededExtension.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreInterpreterNeededExtension.ts @@ -38,6 +38,11 @@ export class USCoreInterpreterNeededExtensionProfile { return profile; } + static is (resource: unknown) : resource is Extension { + if (typeof resource !== "object" || resource === null) return false; + return (resource as { url?: string }).url === USCoreInterpreterNeededExtensionProfile.canonicalUrl; + } + static apply (resource: Extension) : USCoreInterpreterNeededExtensionProfile { resource.url = USCoreInterpreterNeededExtensionProfile.canonicalUrl; Object.assign(resource, { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreRaceExtension.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreRaceExtension.ts index b78f4234..8a73b39b 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreRaceExtension.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreRaceExtension.ts @@ -73,6 +73,11 @@ export class USCoreRaceExtensionProfile { return profile; } + static is (resource: unknown) : resource is Extension { + if (typeof resource !== "object" || resource === null) return false; + return (resource as { url?: string }).url === USCoreRaceExtensionProfile.canonicalUrl; + } + static apply (resource: Extension) : USCoreRaceExtensionProfile { resource.url = USCoreRaceExtensionProfile.canonicalUrl; Object.assign(resource, { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreTribalAffiliationExtension.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreTribalAffiliationExtension.ts index 0de39adc..ae006426 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreTribalAffiliationExtension.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Extension_USCoreTribalAffiliationExtension.ts @@ -66,6 +66,11 @@ export class USCoreTribalAffiliationExtensionProfile { return profile; } + static is (resource: unknown) : resource is Extension { + if (typeof resource !== "object" || resource === null) return false; + return (resource as { url?: string }).url === USCoreTribalAffiliationExtensionProfile.canonicalUrl; + } + static apply (resource: Extension) : USCoreTribalAffiliationExtensionProfile { resource.url = USCoreTribalAffiliationExtensionProfile.canonicalUrl; Object.assign(resource, { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts index 8d75076e..2b7ca6de 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts @@ -79,6 +79,13 @@ export class USCoreBloodPressureProfile { return profile; } + static is (resource: unknown) : resource is Observation { + if (typeof resource !== "object" || resource === null) return false; + const r = resource as { resourceType?: string; meta?: { profile?: string[] } }; + if (r.resourceType !== "Observation") return false; + return (r.meta?.profile ?? []).includes(USCoreBloodPressureProfile.canonicalUrl); + } + static apply (resource: Observation) : USCoreBloodPressureProfile { ensureProfile(resource, USCoreBloodPressureProfile.canonicalUrl); Object.assign(resource, { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts index e7256bd6..118b7acd 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts @@ -64,6 +64,13 @@ export class USCoreBodyWeightProfile { return profile; } + static is (resource: unknown) : resource is Observation { + if (typeof resource !== "object" || resource === null) return false; + const r = resource as { resourceType?: string; meta?: { profile?: string[] } }; + if (r.resourceType !== "Observation") return false; + return (r.meta?.profile ?? []).includes(USCoreBodyWeightProfile.canonicalUrl); + } + static apply (resource: Observation) : USCoreBodyWeightProfile { ensureProfile(resource, USCoreBodyWeightProfile.canonicalUrl); Object.assign(resource, { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts index 846a0aae..0acbe045 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts @@ -65,6 +65,13 @@ export class USCoreVitalSignsProfile { return profile; } + static is (resource: unknown) : resource is Observation { + if (typeof resource !== "object" || resource === null) return false; + const r = resource as { resourceType?: string; meta?: { profile?: string[] } }; + if (r.resourceType !== "Observation") return false; + return (r.meta?.profile ?? []).includes(USCoreVitalSignsProfile.canonicalUrl); + } + static apply (resource: Observation) : USCoreVitalSignsProfile { ensureProfile(resource, USCoreVitalSignsProfile.canonicalUrl); resource.category = ensureSliceDefaults( diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Patient_USCorePatientProfile.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Patient_USCorePatientProfile.ts index 4a469b49..f9d57316 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Patient_USCorePatientProfile.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Patient_USCorePatientProfile.ts @@ -64,6 +64,13 @@ export class USCorePatientProfile { return profile; } + static is (resource: unknown) : resource is Patient { + if (typeof resource !== "object" || resource === null) return false; + const r = resource as { resourceType?: string; meta?: { profile?: string[] } }; + if (r.resourceType !== "Patient") return false; + return (r.meta?.profile ?? []).includes(USCorePatientProfile.canonicalUrl); + } + static apply (resource: Patient) : USCorePatientProfile { ensureProfile(resource, USCorePatientProfile.canonicalUrl); return new USCorePatientProfile(resource); diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index bd7ab5aa..885d4c92 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -1,12 +1,12 @@ import * as Path from "node:path"; import { fileURLToPath } from "node:url"; -import { uppercaseFirstLetter } from "@root/api/writer-generator/utils"; import { Writer, type WriterOptions } from "@root/api/writer-generator/writer"; import { type CanonicalUrl, isChoiceDeclarationField, isComplexTypeIdentifier, isLogicalTypeSchema, + isNestedTypeSchema, isPrimitiveIdentifier, isProfileTypeSchema, isResourceTypeSchema, @@ -41,6 +41,13 @@ export const resolveTsAssets = (fn: string) => { return Path.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "typescript", fn); }; +const leafOf = (path: string[]): string => path[path.length - 1] ?? ""; + +// Schemas that the TS writer renders with a hardcoded `` generic — their IR +// `generic.params` (if any, computed via structural propagation) must be ignored at reference sites +// so we don't emit `` args clashing with the hardcoded `T extends string` declaration. +const TS_HARDCODED_GENERIC_NAMES = new Set(["Reference", "Coding", "CodeableConcept"]); + export type TypeScriptOptions = { lineWidth?: number; /** openResourceTypeSet -- for resource families (Resource, DomainResource) use open set for resourceType field. @@ -204,44 +211,50 @@ export class TypeScript extends Writer { tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedTypeSchema, isFamilyType?: (ref: TypeIdentifier) => boolean, - ) { + ): void { let name: string; // Generic types: Reference, Coding, CodeableConcept const genericTypes = ["Reference", "Coding", "CodeableConcept"]; - if (genericTypes.includes(schema.identifier.name)) { + const isHardcodedGeneric = genericTypes.includes(schema.identifier.name); + if (isHardcodedGeneric) { name = `${schema.identifier.name}`; } else { name = tsResourceName(schema.identifier); } - // Collect fields whose type is a resource type family (has children) - const typeFamilyFields: { fieldName: string; familyTypeName: string }[] = []; - for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { - if (isChoiceDeclarationField(field) || !field.type) continue; - const fieldTypeSchema = tsIndex.resolveType(field.type); - if ( - isSpecializationTypeSchema(fieldTypeSchema) && - (fieldTypeSchema.typeFamily?.resources?.length ?? 0) > 0 - ) { - typeFamilyFields.push({ fieldName: tsFieldName(fieldName), familyTypeName: field.type.name }); + // Generic params come from the IR (populated for all generic-bearing schemas, top-level + nested). + // Hardcoded TS specials (Reference/Coding/CodeableConcept) get their `` above. + const params = isHardcodedGeneric ? [] : (schema.generic?.params ?? []); + + // Per-field substitutions: walk fields once, deciding for each whether its type substitutes + // with a schema param (introduce) or its reference appends args (passthrough). Aligning by + // leaf segment of the param's `path` matches deep origins across nesting hops. + const fieldMap: Record = {}; + const nestedArgsByField: Record = {}; + if (!isHardcodedGeneric) { + for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { + if (isChoiceDeclarationField(field) || !field.type) continue; + const target = tsIndex.resolveType(field.type); + if (!target || TS_HARDCODED_GENERIC_NAMES.has(target.identifier.name)) continue; + const tsName = tsFieldName(fieldName); + const targetParams = + isNestedTypeSchema(target) || isSpecializationTypeSchema(target) + ? target.generic?.params + : undefined; + if (targetParams?.length) { + const args = targetParams.map( + (tp) => params.find((q) => leafOf(q.path) === leafOf(tp.path))?.typeVar ?? tp.typeVar, + ); + nestedArgsByField[tsName] = `<${args.join(", ")}>`; + } else if (isSpecializationTypeSchema(target) && (target.typeFamily?.resources?.length ?? 0) > 0) { + const p = params.find((q) => leafOf(q.path) === fieldName); + if (p) fieldMap[tsName] = p.typeVar; + } } } - - // Build generic params from type-family fields - const genericFieldMap: Record = {}; - if (!genericTypes.includes(schema.identifier.name) && typeFamilyFields.length > 0) { - const [first, ...rest] = typeFamilyFields; - if (first && rest.length === 0) { - genericFieldMap[first.fieldName] = "T"; - name += ``; - } else { - const params = typeFamilyFields.map((tf) => { - const paramName = `T${uppercaseFirstLetter(tf.fieldName)}`; - genericFieldMap[tf.fieldName] = paramName; - return `${paramName} extends ${tf.familyTypeName} = ${tf.familyTypeName}`; - }); - name += `<${params.join(", ")}>`; - } + if (!isHardcodedGeneric && params.length > 0) { + const declParams = params.map((p) => `${p.typeVar} extends ${p.constraint.name} = ${p.constraint.name}`); + name += `<${declParams.join(", ")}>`; } let extendsClause: string | undefined; @@ -282,12 +295,13 @@ export class TypeScript extends Writer { tsName, field, undefined, - genericFieldMap, + fieldMap, isFamilyType, ); const optionalSymbol = field.required ? "" : "?"; const arraySymbol = field.array ? "[]" : ""; - this.lineSM(`${tsName}${optionalSymbol}: ${tsType}${arraySymbol}`); + const nestedArgs = nestedArgsByField[tsName] ?? ""; + this.lineSM(`${tsName}${optionalSymbol}: ${tsType}${nestedArgs}${arraySymbol}`); if (this.withPrimitiveTypeExtension(schema)) { if (isPrimitiveIdentifier(field.type)) { @@ -322,12 +336,11 @@ export class TypeScript extends Writer { tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema, isFamilyType?: (ref: TypeIdentifier) => boolean, - ) { - if (schema.nested) { - for (const subtype of schema.nested) { - this.generateType(tsIndex, subtype, isFamilyType); - this.line(); - } + ): void { + if (!schema.nested) return; + for (const subtype of schema.nested) { + this.generateType(tsIndex, subtype, isFamilyType); + this.line(); } } diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index cd0ccb3e..ea99121d 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -218,10 +218,30 @@ interface PrimitiveTypeSchema { dependencies?: TypeIdentifier[]; } +export type GenericParam = { + typeVar: string; + constraint: TypeIdentifier; + /** Path from this schema down to the typeFamily-rooted field that introduces the param. + * Single segment for a direct introduce (e.g. `["outcome"]` on `BundleEntryResponse`); + * carrier fields are prepended as the param surfaces through passthrough (e.g. + * `["response", "outcome"]` on `BundleEntry`, `["entry", "response", "outcome"]` on + * `Bundle`). Used to align passthrough args across nesting hops. */ + path: string[]; +}; + +/** Generic params a schema exposes — populated during index build (after + * `populateTypeFamily`). Each param either binds directly to a field whose type is a + * type-family root (e.g. `BundleEntry.resource: Resource` → T extends Resource) or + * is inherited from a generic-bearing field's target (passthrough). */ +export type GenericInfo = { + params: GenericParam[]; +}; + export interface NestedTypeSchema { identifier: NestedIdentifier; base: TypeIdentifier; fields: Record; + generic?: GenericInfo; } export interface ProfileTypeSchema { @@ -299,6 +319,7 @@ type SpecializationTypeSchemaBody = { dependencies?: Identifier[]; /** Transitive children grouped by kind (e.g. Resource → { resources: [DomainResource, Patient, …] }) */ typeFamily?: TypeFamily; + generic?: GenericInfo; }; export type TypeFamily = { diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index 87c46dda..a9e45eda 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -9,7 +9,9 @@ import { type ChoiceFieldInstance, type ComplexTypeTypeSchema, type ConstrainedChoiceInfo, + concatIdentifiers, type Field, + type GenericParam, type Identifier, isChoiceDeclarationField, isChoiceInstanceField, @@ -150,6 +152,143 @@ const populateTypeFamily = (schemas: TypeSchema[]): void => { } }; +/////////////////////////////////////////////////////////// +// Generic Params + +type GenericContribution = + | { kind: "introduce"; fieldName: string; constraint: TypeIdentifier } + | { kind: "passthrough"; fieldName: string; params: GenericParam[] }; + +const collectGenericContributions = ( + schema: SpecializationTypeSchema | NestedTypeSchema, + resolveType: (id: TypeIdentifier) => TypeSchema | NestedTypeSchema | undefined, +): GenericContribution[] => { + const contributions: GenericContribution[] = []; + for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { + if (isChoiceDeclarationField(field) || !field.type) continue; + const target = resolveType(field.type); + if (!target) continue; + if (isNestedTypeSchema(target)) { + const params = target.generic?.params; + if (params?.length) contributions.push({ kind: "passthrough", fieldName, params }); + } else if (isSpecializationTypeSchema(target)) { + const params = target.generic?.params; + if (params?.length) { + contributions.push({ kind: "passthrough", fieldName, params }); + } else if ((target.typeFamily?.resources?.length ?? 0) > 0) { + contributions.push({ kind: "introduce", fieldName, constraint: field.type }); + } + } + } + return contributions; +}; + +const samePath = (a: string[], b: string[]): boolean => a.length === b.length && a.every((s, i) => s === b[i]); +const leafOf = (path: string[]): string => path[path.length - 1] ?? ""; + +const renderGenericParams = (contributions: GenericContribution[]): GenericParam[] => { + // Collect raw {path, constraint} pairs. Introduce contributions create single-segment paths + // (the field name); passthrough contributions prepend the carrier field to each inherited + // param's path. Dedup by leaf (last segment) — multiple fields with the same deepest origin + // share one generic param, so callers narrow once and many fields update at once. + type Raw = { path: string[]; constraint: TypeIdentifier }; + const raw: Raw[] = []; + for (const c of contributions) { + if (c.kind === "introduce") { + const path = [c.fieldName]; + if (!raw.find((r) => leafOf(r.path) === leafOf(path))) { + raw.push({ path, constraint: c.constraint }); + } + } else { + for (const np of c.params) { + const path = [c.fieldName, ...np.path]; + if (!raw.find((r) => leafOf(r.path) === leafOf(path))) { + raw.push({ path, constraint: np.constraint }); + } + } + } + } + if (raw.length === 0) return []; + // Single param → "T"; multiple → "T1", "T2", … positional names. + return raw.map((r, i) => ({ + typeVar: raw.length === 1 ? "T" : `T${i + 1}`, + constraint: r.constraint, + path: r.path, + })); +}; + +/** Populate `generic.params` on every specialization schema (top-level and nested). + * A schema becomes generic when one of its fields targets a type-family root + * (introduce) or a generic-bearing schema (passthrough). Param naming: single param + * → "T"; multiple → "T1", "T2", … positional. Each param keeps its `sourceField` + * (the deep field that originally introduced it) so passthrough args align across + * hops. Iterates to a fixpoint so order doesn't matter. */ +const populateGeneric = ( + schemas: TypeSchema[], + resolveType: (id: TypeIdentifier) => TypeSchema | NestedTypeSchema | undefined, +): void => { + type Carrier = SpecializationTypeSchema | NestedTypeSchema; + const carriers: Carrier[] = []; + for (const schema of schemas) { + if (isSpecializationTypeSchema(schema)) { + carriers.push(schema); + for (const nested of schema.nested ?? []) carriers.push(nested); + } else if (isProfileTypeSchema(schema)) { + // Profiles aren't generic-bearing themselves (deliberately out of scope), but their + // nested types follow the same rules as specializations'. + for (const nested of schema.nested ?? []) carriers.push(nested); + } + } + + // Clear stale data — schemas may be mutated by a previous index build (replaceSchemas). + for (const c of carriers) c.generic = undefined; + + const sameParams = (a: GenericParam[], b: GenericParam[]): boolean => { + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + const x = a[i]!; + const y = b[i]!; + if (x.typeVar !== y.typeVar || !samePath(x.path, y.path) || x.constraint.url !== y.constraint.url) + return false; + } + return true; + }; + + // Fixpoint: passthrough between schemas means processing order doesn't fully resolve in one pass. + // Iterate until no changes; bounded by the total number of carriers as a safety cap. + let changed = true; + let iter = 0; + while (changed && iter++ <= carriers.length) { + changed = false; + for (const c of carriers) { + const newParams = renderGenericParams(collectGenericContributions(c, resolveType)); + const oldParams = c.generic?.params ?? []; + if (sameParams(oldParams, newParams)) continue; + c.generic = newParams.length > 0 ? { params: newParams } : undefined; + changed = true; + } + } + + // Ensure each schema's dependency list includes its generic constraint types so writers + // emit the corresponding imports. Top-level specializations have a `dependencies` array; + // nested types don't (their imports come via the parent schema's dependencies). + for (const schema of schemas) { + if (!isSpecializationTypeSchema(schema)) continue; + const constraints: Identifier[] = []; + const collect = (params: GenericParam[]) => { + for (const p of params) { + if (!isNestedIdentifier(p.constraint)) constraints.push(p.constraint); + } + }; + if (schema.generic) collect(schema.generic.params); + for (const nested of schema.nested ?? []) { + if (nested.generic) collect(nested.generic.params); + } + if (constraints.length === 0) continue; + schema.dependencies = concatIdentifiers(schema.dependencies, constraints) ?? schema.dependencies; + } +}; + /////////////////////////////////////////////////////////// // Type Schema Index @@ -234,6 +373,8 @@ export const mkTypeSchemaIndex = ( if (isNestedIdentifier(id)) return nestedIndex[id.url]?.[id.package]; return index[id.url]?.[id.package]; }; + + populateGeneric(schemas, resolveType); const resolveByUrl = (pkgName: PkgName, url: CanonicalUrl): TypeSchema | NestedTypeSchema | undefined => { if (register) { const resolutionTree = register.resolutionTree(); diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index cba8107c..bacdd839 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -1938,6 +1938,37 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "required": false, "excluded": false } + }, + "generic": { + "params": [ + { + "typeVar": "T1", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + }, + "path": [ + "resource" + ] + }, + { + "typeVar": "T2", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + }, + "path": [ + "response", + "outcome" + ] + } + ] } }, { @@ -2124,6 +2155,23 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "excluded": false, "array": false } + }, + "generic": { + "params": [ + { + "typeVar": "T", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + }, + "path": [ + "outcome" + ] + } + ] } }, { @@ -2319,7 +2367,40 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "name": "SearchEntryMode", "url": "urn:fhir:binding:SearchEntryMode" } - ] + ], + "generic": { + "params": [ + { + "typeVar": "T1", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + }, + "path": [ + "entry", + "resource" + ] + }, + { + "typeVar": "T2", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + }, + "path": [ + "entry", + "response", + "outcome" + ] + } + ] + } }" `; @@ -2513,10 +2594,10 @@ exports[`IntrospectionWriter - TypeSchema output Check CodableConcept type schem exports[`IntrospectionWriter - TypeSchema output Check all introspection data in a single ndjson file 1`] = ` "{"identifier":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"OperationOutcome","url":"http://hl7.org/fhir/StructureDefinition/OperationOutcome"},"base":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"DomainResource","url":"http://hl7.org/fhir/StructureDefinition/DomainResource"},"fields":{"issue":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"issue","url":"http://hl7.org/fhir/StructureDefinition/OperationOutcome#issue"},"array":true,"required":true,"excluded":false}},"nested":[{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"issue","url":"http://hl7.org/fhir/StructureDefinition/OperationOutcome#issue"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"severity":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":true,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueSeverity","url":"urn:fhir:binding:IssueSeverity"},"enum":{"isOpen":false,"values":["fatal","error","warning","information"]}},"code":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":true,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueType","url":"urn:fhir:binding:IssueType"},"enum":{"isOpen":false,"values":["invalid","structure","required","value","invariant","security","login","unknown","expired","forbidden","suppressed","processing","not-supported","duplicate","multiple-matches","not-found","deleted","too-long","code-invalid","extension","too-costly","business-rule","conflict","transient","lock-error","no-store","exception","timeout","incomplete","throttled","informational"]}},"details":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"CodeableConcept","url":"http://hl7.org/fhir/StructureDefinition/CodeableConcept"},"required":false,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueDetails","url":"urn:fhir:binding:IssueDetails"}},"diagnostics":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"location":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":true},"expression":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":true}}}],"description":"A collection of error, warning, or information messages that result from a system action.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"CodeableConcept","url":"http://hl7.org/fhir/StructureDefinition/CodeableConcept"},{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"DomainResource","url":"http://hl7.org/fhir/StructureDefinition/DomainResource"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueDetails","url":"urn:fhir:binding:IssueDetails"},{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueSeverity","url":"urn:fhir:binding:IssueSeverity"},{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueType","url":"urn:fhir:binding:IssueType"}]} -{"identifier":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"DomainResource","url":"http://hl7.org/fhir/StructureDefinition/DomainResource"},"base":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"fields":{"text":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},"required":false,"excluded":false,"array":false},"contained":{"type":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"required":false,"excluded":false,"array":true}},"description":"A resource that includes narrative, extensions, and contained resources.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"}],"typeFamily":{"resources":[{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"OperationOutcome","url":"http://hl7.org/fhir/StructureDefinition/OperationOutcome"}]}} +{"identifier":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"DomainResource","url":"http://hl7.org/fhir/StructureDefinition/DomainResource"},"base":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"fields":{"text":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},"required":false,"excluded":false,"array":false},"contained":{"type":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"required":false,"excluded":false,"array":true}},"description":"A resource that includes narrative, extensions, and contained resources.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"}],"typeFamily":{"resources":[{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"OperationOutcome","url":"http://hl7.org/fhir/StructureDefinition/OperationOutcome"}]},"generic":{"params":[{"typeVar":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"path":["contained"]}]}} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{},"description":"Base StructureDefinition for BackboneElement Type: Base definition for all elements that are defined inside a resource - but not those in a data type.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}],"typeFamily":{"complexTypes":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Dosage","url":"http://hl7.org/fhir/StructureDefinition/Dosage"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"ElementDefinition","url":"http://hl7.org/fhir/StructureDefinition/ElementDefinition"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"MarketingStatus","url":"http://hl7.org/fhir/StructureDefinition/MarketingStatus"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Population","url":"http://hl7.org/fhir/StructureDefinition/Population"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"ProdCharacteristic","url":"http://hl7.org/fhir/StructureDefinition/ProdCharacteristic"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"ProductShelfLife","url":"http://hl7.org/fhir/StructureDefinition/ProductShelfLife"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"SubstanceAmount","url":"http://hl7.org/fhir/StructureDefinition/SubstanceAmount"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Timing","url":"http://hl7.org/fhir/StructureDefinition/Timing"}]}} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{"id":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false}},"description":"Base StructureDefinition for Element Type: Base definition for all elements in a resource.","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}],"typeFamily":{"complexTypes":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"CodeableConcept","url":"http://hl7.org/fhir/StructureDefinition/CodeableConcept"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Identifier","url":"http://hl7.org/fhir/StructureDefinition/Identifier"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Signature","url":"http://hl7.org/fhir/StructureDefinition/Signature"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Meta","url":"http://hl7.org/fhir/StructureDefinition/Meta"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Period","url":"http://hl7.org/fhir/StructureDefinition/Period"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Reference","url":"http://hl7.org/fhir/StructureDefinition/Reference"}]}} -{"identifier":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Bundle","url":"http://hl7.org/fhir/StructureDefinition/Bundle"},"base":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"fields":{"identifier":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Identifier","url":"http://hl7.org/fhir/StructureDefinition/Identifier"},"required":false,"excluded":false,"array":false},"type":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":true,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"BundleType","url":"urn:fhir:binding:BundleType"},"enum":{"isOpen":false,"values":["document","message","transaction","transaction-response","batch","batch-response","history","searchset","collection"]}},"timestamp":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"required":false,"excluded":false,"array":false},"total":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"unsignedInt","url":"http://hl7.org/fhir/StructureDefinition/unsignedInt"},"required":false,"excluded":false,"array":false},"link":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"link","url":"http://hl7.org/fhir/StructureDefinition/Bundle#link"},"array":true,"required":false,"excluded":false},"entry":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry"},"array":true,"required":false,"excluded":false},"signature":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Signature","url":"http://hl7.org/fhir/StructureDefinition/Signature"},"required":false,"excluded":false,"array":false}},"nested":[{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"link":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"link","url":"http://hl7.org/fhir/StructureDefinition/Bundle#link"},"required":false,"excluded":false,"array":true},"fullUrl":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":false,"excluded":false,"array":false},"resource":{"type":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"required":false,"excluded":false,"array":false},"search":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.search","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.search"},"array":false,"required":false,"excluded":false},"request":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.request","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.request"},"array":false,"required":false,"excluded":false},"response":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.response","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.response"},"array":false,"required":false,"excluded":false}}},{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.request","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.request"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"method":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":true,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"HTTPVerb","url":"urn:fhir:binding:HTTPVerb"},"enum":{"isOpen":false,"values":["GET","HEAD","POST","PUT","DELETE","PATCH"]}},"url":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":true,"excluded":false,"array":false},"ifNoneMatch":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"ifModifiedSince":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"required":false,"excluded":false,"array":false},"ifMatch":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"ifNoneExist":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false}}},{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.response","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.response"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"status":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":true,"excluded":false,"array":false},"location":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":false,"excluded":false,"array":false},"etag":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"lastModified":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"required":false,"excluded":false,"array":false},"outcome":{"type":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"required":false,"excluded":false,"array":false}}},{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.search","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.search"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"mode":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":false,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"SearchEntryMode","url":"urn:fhir:binding:SearchEntryMode"},"enum":{"isOpen":false,"values":["match","include","outcome"]}},"score":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"decimal","url":"http://hl7.org/fhir/StructureDefinition/decimal"},"required":false,"excluded":false,"array":false}}},{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"link","url":"http://hl7.org/fhir/StructureDefinition/Bundle#link"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"relation":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":true,"excluded":false,"array":false},"url":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":true,"excluded":false,"array":false}}}],"description":"A container for a collection of resources.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"decimal","url":"http://hl7.org/fhir/StructureDefinition/decimal"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Identifier","url":"http://hl7.org/fhir/StructureDefinition/Identifier"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Signature","url":"http://hl7.org/fhir/StructureDefinition/Signature"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"unsignedInt","url":"http://hl7.org/fhir/StructureDefinition/unsignedInt"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},{"kind":"binding","package":"shared","version":"1.0.0","name":"BundleType","url":"urn:fhir:binding:BundleType"},{"kind":"binding","package":"shared","version":"1.0.0","name":"HTTPVerb","url":"urn:fhir:binding:HTTPVerb"},{"kind":"binding","package":"shared","version":"1.0.0","name":"SearchEntryMode","url":"urn:fhir:binding:SearchEntryMode"}]} +{"identifier":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Bundle","url":"http://hl7.org/fhir/StructureDefinition/Bundle"},"base":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"fields":{"identifier":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Identifier","url":"http://hl7.org/fhir/StructureDefinition/Identifier"},"required":false,"excluded":false,"array":false},"type":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":true,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"BundleType","url":"urn:fhir:binding:BundleType"},"enum":{"isOpen":false,"values":["document","message","transaction","transaction-response","batch","batch-response","history","searchset","collection"]}},"timestamp":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"required":false,"excluded":false,"array":false},"total":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"unsignedInt","url":"http://hl7.org/fhir/StructureDefinition/unsignedInt"},"required":false,"excluded":false,"array":false},"link":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"link","url":"http://hl7.org/fhir/StructureDefinition/Bundle#link"},"array":true,"required":false,"excluded":false},"entry":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry"},"array":true,"required":false,"excluded":false},"signature":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Signature","url":"http://hl7.org/fhir/StructureDefinition/Signature"},"required":false,"excluded":false,"array":false}},"nested":[{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"link":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"link","url":"http://hl7.org/fhir/StructureDefinition/Bundle#link"},"required":false,"excluded":false,"array":true},"fullUrl":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":false,"excluded":false,"array":false},"resource":{"type":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"required":false,"excluded":false,"array":false},"search":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.search","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.search"},"array":false,"required":false,"excluded":false},"request":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.request","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.request"},"array":false,"required":false,"excluded":false},"response":{"type":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.response","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.response"},"array":false,"required":false,"excluded":false}},"generic":{"params":[{"typeVar":"T1","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"path":["resource"]},{"typeVar":"T2","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"path":["response","outcome"]}]}},{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.request","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.request"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"method":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":true,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"HTTPVerb","url":"urn:fhir:binding:HTTPVerb"},"enum":{"isOpen":false,"values":["GET","HEAD","POST","PUT","DELETE","PATCH"]}},"url":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":true,"excluded":false,"array":false},"ifNoneMatch":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"ifModifiedSince":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"required":false,"excluded":false,"array":false},"ifMatch":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"ifNoneExist":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false}}},{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.response","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.response"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"status":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":true,"excluded":false,"array":false},"location":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":false,"excluded":false,"array":false},"etag":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"lastModified":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"required":false,"excluded":false,"array":false},"outcome":{"type":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"required":false,"excluded":false,"array":false}},"generic":{"params":[{"typeVar":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"path":["outcome"]}]}},{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"entry.search","url":"http://hl7.org/fhir/StructureDefinition/Bundle#entry.search"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"mode":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":false,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"SearchEntryMode","url":"urn:fhir:binding:SearchEntryMode"},"enum":{"isOpen":false,"values":["match","include","outcome"]}},"score":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"decimal","url":"http://hl7.org/fhir/StructureDefinition/decimal"},"required":false,"excluded":false,"array":false}}},{"identifier":{"kind":"nested","package":"hl7.fhir.r4.core","version":"4.0.1","name":"link","url":"http://hl7.org/fhir/StructureDefinition/Bundle#link"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"fields":{"relation":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":true,"excluded":false,"array":false},"url":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":true,"excluded":false,"array":false}}}],"description":"A container for a collection of resources.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"decimal","url":"http://hl7.org/fhir/StructureDefinition/decimal"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Identifier","url":"http://hl7.org/fhir/StructureDefinition/Identifier"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Signature","url":"http://hl7.org/fhir/StructureDefinition/Signature"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"unsignedInt","url":"http://hl7.org/fhir/StructureDefinition/unsignedInt"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},{"kind":"binding","package":"shared","version":"1.0.0","name":"BundleType","url":"urn:fhir:binding:BundleType"},{"kind":"binding","package":"shared","version":"1.0.0","name":"HTTPVerb","url":"urn:fhir:binding:HTTPVerb"},{"kind":"binding","package":"shared","version":"1.0.0","name":"SearchEntryMode","url":"urn:fhir:binding:SearchEntryMode"}],"generic":{"params":[{"typeVar":"T1","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"path":["entry","resource"]},{"typeVar":"T2","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"path":["entry","response","outcome"]}]}} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{"system":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":false,"excluded":false,"array":false},"version":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"code":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":false,"excluded":false,"array":false},"display":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"userSelected":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"boolean","url":"http://hl7.org/fhir/StructureDefinition/boolean"},"required":false,"excluded":false,"array":false}},"description":"Base StructureDefinition for Coding Type: A reference to a code defined by a terminology system.","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"boolean","url":"http://hl7.org/fhir/StructureDefinition/boolean"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"}]} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"CodeableConcept","url":"http://hl7.org/fhir/StructureDefinition/CodeableConcept"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{"coding":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},"required":false,"excluded":false,"array":true},"text":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false}},"description":"Base StructureDefinition for CodeableConcept Type: A concept that may be defined by a formal reference to a terminology or ontology or may be provided by text.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}]} {"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"description":"Base StructureDefinition for code type: A string which has at least one character and no leading or trailing whitespace and where there is no whitespace other than single spaces in the contents","base":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}]} diff --git a/test/api/write-generator/__snapshots__/typescript.test.ts.snap b/test/api/write-generator/__snapshots__/typescript.test.ts.snap index 70019818..d6aec051 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -128,7 +128,7 @@ export interface CodeableConcept extends Element { " `; -exports[`TypeScript Writer Generator generates BundleEntry with generic type-family parameter 1`] = ` +exports[`TypeScript Writer Generator generates BundleEntry with generic type-family parameters 1`] = ` "// WARNING: This file is autogenerated by @atomic-ehr/codegen. // GitHub: https://github.com/atomic-ehr/codegen // Any manual changes made to this file may be overwritten. @@ -143,12 +143,12 @@ export type { BackboneElement } from "../hl7-fhir-r4-core/BackboneElement"; export type { Identifier } from "../hl7-fhir-r4-core/Identifier"; export type { Signature } from "../hl7-fhir-r4-core/Signature"; -export interface BundleEntry extends BackboneElement { +export interface BundleEntry extends BackboneElement { fullUrl?: string; link?: BundleLink[]; request?: BundleEntryRequest; - resource?: T; - response?: BundleEntryResponse; + resource?: T1; + response?: BundleEntryResponse; search?: BundleEntrySearch; } @@ -180,10 +180,10 @@ export interface BundleLink extends BackboneElement { } // CanonicalURL: http://hl7.org/fhir/StructureDefinition/Bundle (pkg: hl7.fhir.r4.core#4.0.1) -export interface Bundle extends Resource { +export interface Bundle extends Resource { resourceType: "Bundle"; - entry?: BundleEntry[]; + entry?: BundleEntry[]; identifier?: Identifier; link?: BundleLink[]; signature?: Signature; @@ -200,7 +200,7 @@ export const isBundle = (resource: unknown): resource is Bundle => { " `; -exports[`TypeScript Writer Generator generates Bundle with generic entry type (nested generic propagation) 1`] = ` +exports[`TypeScript Writer Generator generates Bundle with inherited generic params from BundleEntry 1`] = ` "// WARNING: This file is autogenerated by @atomic-ehr/codegen. // GitHub: https://github.com/atomic-ehr/codegen // Any manual changes made to this file may be overwritten. @@ -215,12 +215,12 @@ export type { BackboneElement } from "../hl7-fhir-r4-core/BackboneElement"; export type { Identifier } from "../hl7-fhir-r4-core/Identifier"; export type { Signature } from "../hl7-fhir-r4-core/Signature"; -export interface BundleEntry extends BackboneElement { +export interface BundleEntry extends BackboneElement { fullUrl?: string; link?: BundleLink[]; request?: BundleEntryRequest; - resource?: T; - response?: BundleEntryResponse; + resource?: T1; + response?: BundleEntryResponse; search?: BundleEntrySearch; } @@ -252,10 +252,10 @@ export interface BundleLink extends BackboneElement { } // CanonicalURL: http://hl7.org/fhir/StructureDefinition/Bundle (pkg: hl7.fhir.r4.core#4.0.1) -export interface Bundle extends Resource { +export interface Bundle extends Resource { resourceType: "Bundle"; - entry?: BundleEntry[]; + entry?: BundleEntry[]; identifier?: Identifier; link?: BundleLink[]; signature?: Signature; diff --git a/test/api/write-generator/multi-package/__snapshots__/cda.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/cda.test.ts.snap index 8cba7c86..4bb07c68 100644 --- a/test/api/write-generator/multi-package/__snapshots__/cda.test.ts.snap +++ b/test/api/write-generator/multi-package/__snapshots__/cda.test.ts.snap @@ -9,6 +9,7 @@ import type { ANY } from "../hl7-cda-uv-core/ANY"; import type { Authenticator } from "../hl7-cda-uv-core/Authenticator"; import type { Author } from "../hl7-cda-uv-core/Author"; import type { Authorization } from "../hl7-cda-uv-core/Authorization"; +import type { Base } from "../hl7-fhir-r5-core/Base"; import type { CD } from "../hl7-cda-uv-core/CD"; import type { CE } from "../hl7-cda-uv-core/CE"; import type { Component } from "../hl7-cda-uv-core/Component"; @@ -30,33 +31,35 @@ import type { ST } from "../hl7-cda-uv-core/ST"; import type { TS } from "../hl7-cda-uv-core/TS"; import type { Element } from "../hl7-fhir-r5-core/Element"; +export type { Base } from "../hl7-fhir-r5-core/Base"; + // CanonicalURL: http://hl7.org/cda/stds/core/StructureDefinition/ClinicalDocument (pkg: hl7.cda.uv.core#2.0.1-sd) -export interface ClinicalDocument extends ANY { - authenticator?: Authenticator[]; - author: Author[]; +export interface ClinicalDocument extends ANY { + authenticator?: Authenticator[]; + author: Author[]; authorization?: Authorization[]; classCode?: string; _classCode?: Element; code: CE; - component: Component; - componentOf?: ComponentOf; + component: Component; + componentOf?: ComponentOf; confidentialityCode: CE; copyTime?: TS; custodian: Custodian; - dataEnterer?: DataEnterer; - documentationOf?: DocumentationOf[]; + dataEnterer?: DataEnterer; + documentationOf?: DocumentationOf[]; effectiveTime: TS; id: II; - informant?: Informant[]; - informationRecipient?: InformationRecipient[]; + informant?: Informant[]; + informationRecipient?: InformationRecipient[]; inFulfillmentOf?: InFulfillmentOf[]; languageCode?: CS; - legalAuthenticator?: LegalAuthenticator; + legalAuthenticator?: LegalAuthenticator; moodCode?: ("INT" | "APT" | "ARQ" | "PRMS" | "PRP" | "RQO" | "SLOT" | "DEF" | "EVN" | "EVN.CRT" | "GOL" | "OPT" | "PERM" | "PERMRQ"); _moodCode?: Element; - participant?: Participant1[]; + participant?: Participant1[]; realmCode?: CS[]; - recordTarget: RecordTarget[]; + recordTarget: RecordTarget[]; relatedDocument?: RelatedDocument[]; sdtcCategory?: CD[]; sdtcStatusCode?: CS; @@ -78,6 +81,7 @@ from __future__ import annotations from pydantic import BaseModel, ConfigDict, Field, PositiveInt from typing import Any, List as PyList, Literal +from fhir_types.hl7_fhir_r5_core.base import Base class ClinicalDocument(ANY): diff --git a/test/api/write-generator/typescript.test.ts b/test/api/write-generator/typescript.test.ts index 65fc7c6c..cd51945e 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -27,14 +27,21 @@ describe("TypeScript Writer Generator", async () => { expect(ccTs).toContain("coding?: Coding[]"); expect(ccTs).toMatchSnapshot(); }); - it("generates BundleEntry with generic type-family parameter", async () => { + it("generates BundleEntry with generic type-family parameters", async () => { const bundleTs = files["generated/types/hl7-fhir-r4-core/Bundle.ts"]; - expect(bundleTs).toContain("export interface BundleEntry"); - expect(bundleTs).toContain("resource?: T"); + expect(bundleTs).toContain( + "export interface BundleEntry", + ); + expect(bundleTs).toContain("resource?: T1"); + expect(bundleTs).toContain("response?: BundleEntryResponse"); expect(bundleTs).toMatchSnapshot(); }); - it("generates Bundle with generic entry type (nested generic propagation)", async () => { + it("generates Bundle with inherited generic params from BundleEntry", async () => { const bundleTs = files["generated/types/hl7-fhir-r4-core/Bundle.ts"]; + expect(bundleTs).toContain( + "export interface Bundle", + ); + expect(bundleTs).toContain("entry?: BundleEntry[]"); expect(bundleTs).toMatchSnapshot(); }); it("generates BundleEntryResponse with generic type-family parameter", async () => {