From 4827da2cfe881dd935bf101dc9d4060b8f9f540f Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 22 Apr 2026 15:52:40 +0200 Subject: [PATCH 01/21] TS: propagate generic type params through nested type references Parent interfaces now become generic when any of their fields reference a generic nested type, and the field reference carries the type parameter through. Before: `Bundle { entry?: BundleEntry[] }` (always BundleEntry) After: `Bundle { entry?: BundleEntry[] }` Default `= Resource` keeps existing call sites working. Callers can now narrow, e.g. `Bundle`. Refactored the generic-params computation in generateType() into a reusable helper (computeGenericInfo) and threaded per-nested-type info from generateNestedTypes() back into the parent generation. --- src/api/writer-generator/typescript/writer.ts | 117 ++++++++++++------ 1 file changed, 81 insertions(+), 36 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index bd7ab5aa..ecdaf3b8 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -7,6 +7,7 @@ import { isChoiceDeclarationField, isComplexTypeIdentifier, isLogicalTypeSchema, + isNestedIdentifier, isPrimitiveIdentifier, isProfileTypeSchema, isResourceTypeSchema, @@ -41,6 +42,65 @@ export const resolveTsAssets = (fn: string) => { return Path.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "typescript", fn); }; +export type GenericInfo = { + paramList: { name: string; constraint: string }[]; + /** fieldName (ts-name) -> generic param name, used by resolveFieldTsType to substitute the field's type */ + fieldMap: Record; + /** fieldName (ts-name) -> generic args suffix to append (e.g. "") when the field references a generic nested type */ + nestedArgsByField: Record; +}; + +const computeGenericInfo = ( + tsIndex: TypeSchemaIndex, + schema: SpecializationTypeSchema | NestedTypeSchema, + nestedGenericInfos: Record, +): GenericInfo => { + const typeFamilyFields: { fieldName: string; familyTypeName: string }[] = []; + const nestedFields: { fieldName: string; nestedName: string; info: GenericInfo }[] = []; + for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { + if (isChoiceDeclarationField(field) || !field.type) continue; + const tsName = tsFieldName(fieldName); + if (isNestedIdentifier(field.type)) { + const nestedInfo = nestedGenericInfos[field.type.name]; + if (nestedInfo && nestedInfo.paramList.length > 0) { + nestedFields.push({ fieldName: tsName, nestedName: field.type.name, info: nestedInfo }); + } + continue; + } + const fieldTypeSchema = tsIndex.resolveType(field.type); + if (isSpecializationTypeSchema(fieldTypeSchema) && (fieldTypeSchema.typeFamily?.resources?.length ?? 0) > 0) { + typeFamilyFields.push({ fieldName: tsName, familyTypeName: field.type.name }); + } + } + + const fieldMap: Record = {}; + const nestedArgsByField: Record = {}; + const paramList: { name: string; constraint: string }[] = []; + const [first] = typeFamilyFields; + if (typeFamilyFields.length === 1 && nestedFields.length === 0 && first) { + fieldMap[first.fieldName] = "T"; + paramList.push({ name: "T", constraint: first.familyTypeName }); + } else { + for (const tf of typeFamilyFields) { + const paramName = `T${uppercaseFirstLetter(tf.fieldName)}`; + fieldMap[tf.fieldName] = paramName; + paramList.push({ name: paramName, constraint: tf.familyTypeName }); + } + } + // Inherit nested type's generic params by reusing the same parameter names. + // Only propagate when the parent doesn't already define a param with the same name/constraint. + for (const nf of nestedFields) { + const passThrough: string[] = []; + for (const np of nf.info.paramList) { + const existing = paramList.find((p) => p.name === np.name); + if (!existing) paramList.push({ name: np.name, constraint: np.constraint }); + passThrough.push(np.name); + } + nestedArgsByField[nf.fieldName] = `<${passThrough.join(", ")}>`; + } + return { paramList, fieldMap, nestedArgsByField }; +}; + export type TypeScriptOptions = { lineWidth?: number; /** openResourceTypeSet -- for resource families (Resource, DomainResource) use open set for resourceType field. @@ -204,7 +264,8 @@ export class TypeScript extends Writer { tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedTypeSchema, isFamilyType?: (ref: TypeIdentifier) => boolean, - ) { + nestedGenericInfos: Record = {}, + ): GenericInfo { let name: string; // Generic types: Reference, Coding, CodeableConcept const genericTypes = ["Reference", "Coding", "CodeableConcept"]; @@ -214,34 +275,13 @@ export class TypeScript extends Writer { 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 }); - } - } - - // 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(", ")}>`; - } + const isHardcodedGeneric = genericTypes.includes(schema.identifier.name); + const genericInfo: GenericInfo = isHardcodedGeneric + ? { paramList: [], fieldMap: {}, nestedArgsByField: {} } + : computeGenericInfo(tsIndex, schema, nestedGenericInfos); + if (!isHardcodedGeneric && genericInfo.paramList.length > 0) { + const declParams = genericInfo.paramList.map((p) => `${p.name} extends ${p.constraint} = ${p.constraint}`); + name += `<${declParams.join(", ")}>`; } let extendsClause: string | undefined; @@ -250,7 +290,7 @@ export class TypeScript extends Writer { this.debugComment(schema.identifier); if (!schema.fields && !extendsClause && !isResourceTypeSchema(schema)) { this.lineSM(`export type ${name} = object`); - return; + return genericInfo; } this.curlyBlock(["export", "interface", name, extendsClause], () => { if (isResourceTypeSchema(schema)) { @@ -282,12 +322,13 @@ export class TypeScript extends Writer { tsName, field, undefined, - genericFieldMap, + genericInfo.fieldMap, isFamilyType, ); const optionalSymbol = field.required ? "" : "?"; const arraySymbol = field.array ? "[]" : ""; - this.lineSM(`${tsName}${optionalSymbol}: ${tsType}${arraySymbol}`); + const nestedArgs = genericInfo.nestedArgsByField[tsName] ?? ""; + this.lineSM(`${tsName}${optionalSymbol}: ${tsType}${nestedArgs}${arraySymbol}`); if (this.withPrimitiveTypeExtension(schema)) { if (isPrimitiveIdentifier(field.type)) { @@ -296,6 +337,7 @@ export class TypeScript extends Writer { } } }); + return genericInfo; } withPrimitiveTypeExtension(schema: TypeSchema | NestedTypeSchema): boolean { @@ -322,13 +364,16 @@ export class TypeScript extends Writer { tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema, isFamilyType?: (ref: TypeIdentifier) => boolean, - ) { + ): Record { + const nestedGenericInfos: Record = {}; if (schema.nested) { for (const subtype of schema.nested) { - this.generateType(tsIndex, subtype, isFamilyType); + const info = this.generateType(tsIndex, subtype, isFamilyType, nestedGenericInfos); + nestedGenericInfos[subtype.identifier.name] = info; this.line(); } } + return nestedGenericInfos; } generateResourceModule(tsIndex: TypeSchemaIndex, schema: TypeSchema) { @@ -347,13 +392,13 @@ export class TypeScript extends Writer { this.generateDisclaimer(); this.generateDependenciesImports(tsIndex, schema); this.generateComplexTypeReexports(schema); - this.generateNestedTypes(tsIndex, schema, isFamilyType); + const nestedGenericInfos = this.generateNestedTypes(tsIndex, schema, isFamilyType); this.comment( "CanonicalURL:", schema.identifier.url, `(pkg: ${packageMetaToFhir(packageMeta(schema))})`, ); - this.generateType(tsIndex, schema, isFamilyType); + this.generateType(tsIndex, schema, isFamilyType, nestedGenericInfos); this.generateResourceTypePredicate(schema); }); } else { From f3bbf649c84d308b328bdc69b4be918190c5fe26 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 22 Apr 2026 15:52:41 +0200 Subject: [PATCH 02/21] TS: regenerate Bundle and add Bundle demo tests - Bundle.ts now declares `Bundle` with `entry?: BundleEntry[]`. - Added demo tests showing discriminated-union narrowing via `Bundle` and backwards-compat default. --- .../fhir-types/hl7-fhir-r4-core/Bundle.ts | 4 +- examples/typescript-r4/resource.test.ts | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) 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..dfb13c8c 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 @@ -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..27b8a265 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 }, + ], + }; + + // Discriminated-union narrowing works without a `r is Observation` predicate + const observations: Observation[] = (bundle.entry ?? []) + .map((e) => e.resource) + .filter((r): r is Observation => 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" }; From 01bf5df4732ea25f19c6f446e25c941dbcdce0ed Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Tue, 28 Apr 2026 16:18:10 +0200 Subject: [PATCH 03/21] test: add Bundle propagation unit test and fix no-predicate demo --- examples/typescript-r4/resource.test.ts | 4 ++-- test/api/write-generator/typescript.test.ts | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/typescript-r4/resource.test.ts b/examples/typescript-r4/resource.test.ts index 27b8a265..e700d3e6 100644 --- a/examples/typescript-r4/resource.test.ts +++ b/examples/typescript-r4/resource.test.ts @@ -135,10 +135,10 @@ test("Bundle narrows entry resources without type predicates", () => { ], }; - // Discriminated-union narrowing works without a `r is Observation` predicate + // 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 is Observation => r?.resourceType === "Observation"); + .filter((r) => r?.resourceType === "Observation"); expect(observations).toHaveLength(1); expect(observations[0]!.id).toBe("glucose-obs-1"); diff --git a/test/api/write-generator/typescript.test.ts b/test/api/write-generator/typescript.test.ts index 65fc7c6c..039077d3 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -37,6 +37,11 @@ describe("TypeScript Writer Generator", async () => { const bundleTs = files["generated/types/hl7-fhir-r4-core/Bundle.ts"]; expect(bundleTs).toMatchSnapshot(); }); + it("generates Bundle with generic entry type (nested generic propagation)", async () => { + const bundleTs = files["generated/types/hl7-fhir-r4-core/Bundle.ts"]; + expect(bundleTs).toContain("export interface Bundle"); + expect(bundleTs).toContain("entry?: BundleEntry[]"); + }); it("generates BundleEntryResponse with generic type-family parameter", async () => { const bundleTs = files["generated/types/hl7-fhir-r4-core/Bundle.ts"]; expect(bundleTs).toContain("export interface BundleEntryResponse"); From 55cd8dc854a5d48b4e48299c4a0e65d91a6f56be Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Tue, 28 Apr 2026 16:56:18 +0200 Subject: [PATCH 04/21] ref: inline generic-info computation in TS writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the standalone computeGenericInfo helper and the GenericInfo type in favor of inlining the (small) logic directly into generateType. The helper duplicated the existing typeFamilyFields detection that already worked; the genuinely new bit is just the nested-type pass-through. generateType now returns the parent's GenericParam[] (only paramList is needed by callers — fieldMap and nestedArgsByField are local concerns), and generateNestedTypes threads a Record back to the parent. Net change vs main: +61/-32 (was +81/-36). No behavior change. --- src/api/writer-generator/typescript/writer.ts | 140 ++++++++---------- 1 file changed, 62 insertions(+), 78 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index ecdaf3b8..01d2de76 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -42,64 +42,7 @@ export const resolveTsAssets = (fn: string) => { return Path.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "typescript", fn); }; -export type GenericInfo = { - paramList: { name: string; constraint: string }[]; - /** fieldName (ts-name) -> generic param name, used by resolveFieldTsType to substitute the field's type */ - fieldMap: Record; - /** fieldName (ts-name) -> generic args suffix to append (e.g. "") when the field references a generic nested type */ - nestedArgsByField: Record; -}; - -const computeGenericInfo = ( - tsIndex: TypeSchemaIndex, - schema: SpecializationTypeSchema | NestedTypeSchema, - nestedGenericInfos: Record, -): GenericInfo => { - const typeFamilyFields: { fieldName: string; familyTypeName: string }[] = []; - const nestedFields: { fieldName: string; nestedName: string; info: GenericInfo }[] = []; - for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { - if (isChoiceDeclarationField(field) || !field.type) continue; - const tsName = tsFieldName(fieldName); - if (isNestedIdentifier(field.type)) { - const nestedInfo = nestedGenericInfos[field.type.name]; - if (nestedInfo && nestedInfo.paramList.length > 0) { - nestedFields.push({ fieldName: tsName, nestedName: field.type.name, info: nestedInfo }); - } - continue; - } - const fieldTypeSchema = tsIndex.resolveType(field.type); - if (isSpecializationTypeSchema(fieldTypeSchema) && (fieldTypeSchema.typeFamily?.resources?.length ?? 0) > 0) { - typeFamilyFields.push({ fieldName: tsName, familyTypeName: field.type.name }); - } - } - - const fieldMap: Record = {}; - const nestedArgsByField: Record = {}; - const paramList: { name: string; constraint: string }[] = []; - const [first] = typeFamilyFields; - if (typeFamilyFields.length === 1 && nestedFields.length === 0 && first) { - fieldMap[first.fieldName] = "T"; - paramList.push({ name: "T", constraint: first.familyTypeName }); - } else { - for (const tf of typeFamilyFields) { - const paramName = `T${uppercaseFirstLetter(tf.fieldName)}`; - fieldMap[tf.fieldName] = paramName; - paramList.push({ name: paramName, constraint: tf.familyTypeName }); - } - } - // Inherit nested type's generic params by reusing the same parameter names. - // Only propagate when the parent doesn't already define a param with the same name/constraint. - for (const nf of nestedFields) { - const passThrough: string[] = []; - for (const np of nf.info.paramList) { - const existing = paramList.find((p) => p.name === np.name); - if (!existing) paramList.push({ name: np.name, constraint: np.constraint }); - passThrough.push(np.name); - } - nestedArgsByField[nf.fieldName] = `<${passThrough.join(", ")}>`; - } - return { paramList, fieldMap, nestedArgsByField }; -}; +type GenericParam = { name: string; constraint: string }; export type TypeScriptOptions = { lineWidth?: number; @@ -264,23 +207,64 @@ export class TypeScript extends Writer { tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedTypeSchema, isFamilyType?: (ref: TypeIdentifier) => boolean, - nestedGenericInfos: Record = {}, - ): GenericInfo { + nestedGenericParams: Record = {}, + ): GenericParam[] { 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); } - const isHardcodedGeneric = genericTypes.includes(schema.identifier.name); - const genericInfo: GenericInfo = isHardcodedGeneric - ? { paramList: [], fieldMap: {}, nestedArgsByField: {} } - : computeGenericInfo(tsIndex, schema, nestedGenericInfos); - if (!isHardcodedGeneric && genericInfo.paramList.length > 0) { - const declParams = genericInfo.paramList.map((p) => `${p.name} extends ${p.constraint} = ${p.constraint}`); + // Collect type-family fields (e.g. `resource: Resource` -> needs T extends Resource) + // and references to generic nested types (e.g. `entry: BundleEntry` when BundleEntry is generic). + const typeFamilyFields: { fieldName: string; familyTypeName: string }[] = []; + const nestedFields: { fieldName: string; params: GenericParam[] }[] = []; + if (!isHardcodedGeneric) { + for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { + if (isChoiceDeclarationField(field) || !field.type) continue; + const tsName = tsFieldName(fieldName); + if (isNestedIdentifier(field.type)) { + const params = nestedGenericParams[field.type.name]; + if (params?.length) nestedFields.push({ fieldName: tsName, params }); + continue; + } + const fieldTypeSchema = tsIndex.resolveType(field.type); + if ( + isSpecializationTypeSchema(fieldTypeSchema) && + (fieldTypeSchema.typeFamily?.resources?.length ?? 0) > 0 + ) { + typeFamilyFields.push({ fieldName: tsName, familyTypeName: field.type.name }); + } + } + } + + // Build generic params from type-family fields, then pass-through nested-type params. + const fieldMap: Record = {}; + const paramList: GenericParam[] = []; + const [first] = typeFamilyFields; + if (typeFamilyFields.length === 1 && nestedFields.length === 0 && first) { + fieldMap[first.fieldName] = "T"; + paramList.push({ name: "T", constraint: first.familyTypeName }); + } else { + for (const tf of typeFamilyFields) { + const paramName = `T${uppercaseFirstLetter(tf.fieldName)}`; + fieldMap[tf.fieldName] = paramName; + paramList.push({ name: paramName, constraint: tf.familyTypeName }); + } + } + const nestedArgsByField: Record = {}; + for (const nf of nestedFields) { + for (const np of nf.params) { + if (!paramList.find((p) => p.name === np.name)) paramList.push(np); + } + nestedArgsByField[nf.fieldName] = `<${nf.params.map((p) => p.name).join(", ")}>`; + } + if (!isHardcodedGeneric && paramList.length > 0) { + const declParams = paramList.map((p) => `${p.name} extends ${p.constraint} = ${p.constraint}`); name += `<${declParams.join(", ")}>`; } @@ -290,7 +274,7 @@ export class TypeScript extends Writer { this.debugComment(schema.identifier); if (!schema.fields && !extendsClause && !isResourceTypeSchema(schema)) { this.lineSM(`export type ${name} = object`); - return genericInfo; + return paramList; } this.curlyBlock(["export", "interface", name, extendsClause], () => { if (isResourceTypeSchema(schema)) { @@ -322,12 +306,12 @@ export class TypeScript extends Writer { tsName, field, undefined, - genericInfo.fieldMap, + fieldMap, isFamilyType, ); const optionalSymbol = field.required ? "" : "?"; const arraySymbol = field.array ? "[]" : ""; - const nestedArgs = genericInfo.nestedArgsByField[tsName] ?? ""; + const nestedArgs = nestedArgsByField[tsName] ?? ""; this.lineSM(`${tsName}${optionalSymbol}: ${tsType}${nestedArgs}${arraySymbol}`); if (this.withPrimitiveTypeExtension(schema)) { @@ -337,7 +321,7 @@ export class TypeScript extends Writer { } } }); - return genericInfo; + return paramList; } withPrimitiveTypeExtension(schema: TypeSchema | NestedTypeSchema): boolean { @@ -364,16 +348,16 @@ export class TypeScript extends Writer { tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema, isFamilyType?: (ref: TypeIdentifier) => boolean, - ): Record { - const nestedGenericInfos: Record = {}; + ): Record { + const nestedGenericParams: Record = {}; if (schema.nested) { for (const subtype of schema.nested) { - const info = this.generateType(tsIndex, subtype, isFamilyType, nestedGenericInfos); - nestedGenericInfos[subtype.identifier.name] = info; + const params = this.generateType(tsIndex, subtype, isFamilyType, nestedGenericParams); + nestedGenericParams[subtype.identifier.name] = params; this.line(); } } - return nestedGenericInfos; + return nestedGenericParams; } generateResourceModule(tsIndex: TypeSchemaIndex, schema: TypeSchema) { @@ -392,13 +376,13 @@ export class TypeScript extends Writer { this.generateDisclaimer(); this.generateDependenciesImports(tsIndex, schema); this.generateComplexTypeReexports(schema); - const nestedGenericInfos = this.generateNestedTypes(tsIndex, schema, isFamilyType); + const nestedGenericParams = this.generateNestedTypes(tsIndex, schema, isFamilyType); this.comment( "CanonicalURL:", schema.identifier.url, `(pkg: ${packageMetaToFhir(packageMeta(schema))})`, ); - this.generateType(tsIndex, schema, isFamilyType, nestedGenericInfos); + this.generateType(tsIndex, schema, isFamilyType, nestedGenericParams); this.generateResourceTypePredicate(schema); }); } else { From 95d03f642d5481790cb1a65fcf12631f1257417f Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Tue, 28 Apr 2026 17:05:31 +0200 Subject: [PATCH 05/21] test: snapshot Bundle.ts with generic propagation --- .../__snapshots__/typescript.test.ts.snap | 72 +++++++++++++++++++ test/api/write-generator/typescript.test.ts | 1 + 2 files changed, 73 insertions(+) diff --git a/test/api/write-generator/__snapshots__/typescript.test.ts.snap b/test/api/write-generator/__snapshots__/typescript.test.ts.snap index 70019818..132ec7ab 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -2314,3 +2314,75 @@ export { USCoreTribalAffiliationExtensionProfile } from "./Extension_USCoreTriba export { USCoreVitalSignsProfile } from "./Observation_USCoreVitalSignsProfile"; " `; + +exports[`TypeScript Writer Generator generates Bundle with generic entry type (nested generic propagation) 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. + +import type { BackboneElement } from "../hl7-fhir-r4-core/BackboneElement"; +import type { Identifier } from "../hl7-fhir-r4-core/Identifier"; +import type { Resource } from "../hl7-fhir-r4-core/Resource"; +import type { Signature } from "../hl7-fhir-r4-core/Signature"; + +import type { Element } from "../hl7-fhir-r4-core/Element"; +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 { + fullUrl?: string; + link?: BundleLink[]; + request?: BundleEntryRequest; + resource?: T; + response?: BundleEntryResponse; + search?: BundleEntrySearch; +} + +export interface BundleEntryRequest extends BackboneElement { + ifMatch?: string; + ifModifiedSince?: string; + ifNoneExist?: string; + ifNoneMatch?: string; + method: ("GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH"); + url: string; +} + +export interface BundleEntryResponse extends BackboneElement { + etag?: string; + lastModified?: string; + location?: string; + outcome?: T; + status: string; +} + +export interface BundleEntrySearch extends BackboneElement { + mode?: ("match" | "include" | "outcome"); + score?: number; +} + +export interface BundleLink extends BackboneElement { + relation: string; + url: string; +} + +// CanonicalURL: http://hl7.org/fhir/StructureDefinition/Bundle (pkg: hl7.fhir.r4.core#4.0.1) +export interface Bundle extends Resource { + resourceType: "Bundle"; + + entry?: BundleEntry[]; + identifier?: Identifier; + link?: BundleLink[]; + signature?: Signature; + timestamp?: string; + _timestamp?: Element; + total?: number; + _total?: Element; + type: ("document" | "message" | "transaction" | "transaction-response" | "batch" | "batch-response" | "history" | "searchset" | "collection"); + _type?: Element; +} +export const isBundle = (resource: unknown): resource is Bundle => { + return resource !== null && typeof resource === "object" && (resource as {resourceType: string}).resourceType === "Bundle"; +} +" +`; diff --git a/test/api/write-generator/typescript.test.ts b/test/api/write-generator/typescript.test.ts index 039077d3..3c827a07 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -41,6 +41,7 @@ describe("TypeScript Writer Generator", 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 () => { const bundleTs = files["generated/types/hl7-fhir-r4-core/Bundle.ts"]; From 1a90da884bc0fb93faebba2e3e9094a52961749c Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 29 Apr 2026 15:50:33 +0200 Subject: [PATCH 06/21] ref: unify generic-contribution loop in TS writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapse the two-list collection (typeFamilyFields + nestedFields) into a single discriminated Contribution[] driven by tsIndex.resolveType, which handles nested and non-nested identifiers uniformly. - introduce: field type resolves to a typeFamily root → bind a fresh param - passthrough: field type resolves to a generic nested type → inherit its params Extract the collection into a free helper (collectGenericContributions) to keep generateType under the cognitive-complexity cap. Render pass becomes one loop over contributions; naming policy (T vs TFieldName) keys off the contribution shape. --- src/api/writer-generator/typescript/writer.ts | 85 ++++++++++--------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index 01d2de76..1994b1f3 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -7,7 +7,7 @@ import { isChoiceDeclarationField, isComplexTypeIdentifier, isLogicalTypeSchema, - isNestedIdentifier, + isNestedTypeSchema, isPrimitiveIdentifier, isProfileTypeSchema, isResourceTypeSchema, @@ -44,6 +44,31 @@ export const resolveTsAssets = (fn: string) => { type GenericParam = { name: string; constraint: string }; +type Contribution = + | { kind: "introduce"; fieldName: string; constraint: string } + | { kind: "passthrough"; fieldName: string; params: GenericParam[] }; + +const collectGenericContributions = ( + tsIndex: TypeSchemaIndex, + schema: SpecializationTypeSchema | NestedTypeSchema, + nestedGenericParams: Record, +): Contribution[] => { + const contributions: Contribution[] = []; + for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { + if (isChoiceDeclarationField(field) || !field.type) continue; + const tsName = tsFieldName(fieldName); + const target = tsIndex.resolveType(field.type); + if (!target) continue; + if (isNestedTypeSchema(target)) { + const params = nestedGenericParams[target.identifier.name]; + if (params?.length) contributions.push({ kind: "passthrough", fieldName: tsName, params }); + } else if (isSpecializationTypeSchema(target) && (target.typeFamily?.resources?.length ?? 0) > 0) { + contributions.push({ kind: "introduce", fieldName: tsName, constraint: field.type.name }); + } + } + return contributions; +}; + export type TypeScriptOptions = { lineWidth?: number; /** openResourceTypeSet -- for resource families (Resource, DomainResource) use open set for resourceType field. @@ -219,49 +244,29 @@ export class TypeScript extends Writer { name = tsResourceName(schema.identifier); } - // Collect type-family fields (e.g. `resource: Resource` -> needs T extends Resource) - // and references to generic nested types (e.g. `entry: BundleEntry` when BundleEntry is generic). - const typeFamilyFields: { fieldName: string; familyTypeName: string }[] = []; - const nestedFields: { fieldName: string; params: GenericParam[] }[] = []; - if (!isHardcodedGeneric) { - for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { - if (isChoiceDeclarationField(field) || !field.type) continue; - const tsName = tsFieldName(fieldName); - if (isNestedIdentifier(field.type)) { - const params = nestedGenericParams[field.type.name]; - if (params?.length) nestedFields.push({ fieldName: tsName, params }); - continue; - } - const fieldTypeSchema = tsIndex.resolveType(field.type); - if ( - isSpecializationTypeSchema(fieldTypeSchema) && - (fieldTypeSchema.typeFamily?.resources?.length ?? 0) > 0 - ) { - typeFamilyFields.push({ fieldName: tsName, familyTypeName: field.type.name }); - } - } - } + // Per-field generic contributions: either introduce a fresh param (typeFamily-rooted field type) + // or inherit a nested type's existing params (pass-through). + const contributions = isHardcodedGeneric + ? [] + : collectGenericContributions(tsIndex, schema, nestedGenericParams); - // Build generic params from type-family fields, then pass-through nested-type params. + // Render contributions into paramList + per-field substitution maps. const fieldMap: Record = {}; - const paramList: GenericParam[] = []; - const [first] = typeFamilyFields; - if (typeFamilyFields.length === 1 && nestedFields.length === 0 && first) { - fieldMap[first.fieldName] = "T"; - paramList.push({ name: "T", constraint: first.familyTypeName }); - } else { - for (const tf of typeFamilyFields) { - const paramName = `T${uppercaseFirstLetter(tf.fieldName)}`; - fieldMap[tf.fieldName] = paramName; - paramList.push({ name: paramName, constraint: tf.familyTypeName }); - } - } const nestedArgsByField: Record = {}; - for (const nf of nestedFields) { - for (const np of nf.params) { - if (!paramList.find((p) => p.name === np.name)) paramList.push(np); + const paramList: GenericParam[] = []; + const introduceCount = contributions.filter((c) => c.kind === "introduce").length; + const useShortName = introduceCount === 1 && contributions.length === 1; + for (const c of contributions) { + if (c.kind === "introduce") { + const paramName = useShortName ? "T" : `T${uppercaseFirstLetter(c.fieldName)}`; + fieldMap[c.fieldName] = paramName; + paramList.push({ name: paramName, constraint: c.constraint }); + } else { + for (const np of c.params) { + if (!paramList.find((p) => p.name === np.name)) paramList.push(np); + } + nestedArgsByField[c.fieldName] = `<${c.params.map((p) => p.name).join(", ")}>`; } - nestedArgsByField[nf.fieldName] = `<${nf.params.map((p) => p.name).join(", ")}>`; } if (!isHardcodedGeneric && paramList.length > 0) { const declParams = paramList.map((p) => `${p.name} extends ${p.constraint} = ${p.constraint}`); From cbd2d5eadc4ebd54f4f0ae02154a0757e5b4aa46 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 29 Apr 2026 15:50:39 +0200 Subject: [PATCH 07/21] test: snapshot Coding, CodeableConcept, BundleEntry generic emit --- .../__snapshots__/typescript.test.ts.snap | 117 ++++++++++++++++++ test/api/write-generator/typescript.test.ts | 4 - 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/test/api/write-generator/__snapshots__/typescript.test.ts.snap b/test/api/write-generator/__snapshots__/typescript.test.ts.snap index 132ec7ab..31962ed1 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -2386,3 +2386,120 @@ export const isBundle = (resource: unknown): resource is Bundle => { } " `; + +exports[`TypeScript Writer Generator generates Coding with generic parameter 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. + +import type { Element } from "../hl7-fhir-r4-core/Element"; + +export type { Element } from "../hl7-fhir-r4-core/Element"; + +// CanonicalURL: http://hl7.org/fhir/StructureDefinition/Coding (pkg: hl7.fhir.r4.core#4.0.1) +export interface Coding extends Element { + code?: T; + _code?: Element; + display?: string; + _display?: Element; + system?: string; + _system?: Element; + userSelected?: boolean; + _userSelected?: Element; + version?: string; + _version?: Element; +} +" +`; + +exports[`TypeScript Writer Generator generates CodeableConcept with generic parameter 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. + +import type { Coding } from "../hl7-fhir-r4-core/Coding"; +import type { Element } from "../hl7-fhir-r4-core/Element"; + +export type { Coding } from "../hl7-fhir-r4-core/Coding"; +export type { Element } from "../hl7-fhir-r4-core/Element"; + +// CanonicalURL: http://hl7.org/fhir/StructureDefinition/CodeableConcept (pkg: hl7.fhir.r4.core#4.0.1) +export interface CodeableConcept extends Element { + coding?: Coding[]; + text?: string; + _text?: Element; +} +" +`; + +exports[`TypeScript Writer Generator generates BundleEntry with generic type-family parameter 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. + +import type { BackboneElement } from "../hl7-fhir-r4-core/BackboneElement"; +import type { Identifier } from "../hl7-fhir-r4-core/Identifier"; +import type { Resource } from "../hl7-fhir-r4-core/Resource"; +import type { Signature } from "../hl7-fhir-r4-core/Signature"; + +import type { Element } from "../hl7-fhir-r4-core/Element"; +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 { + fullUrl?: string; + link?: BundleLink[]; + request?: BundleEntryRequest; + resource?: T; + response?: BundleEntryResponse; + search?: BundleEntrySearch; +} + +export interface BundleEntryRequest extends BackboneElement { + ifMatch?: string; + ifModifiedSince?: string; + ifNoneExist?: string; + ifNoneMatch?: string; + method: ("GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH"); + url: string; +} + +export interface BundleEntryResponse extends BackboneElement { + etag?: string; + lastModified?: string; + location?: string; + outcome?: T; + status: string; +} + +export interface BundleEntrySearch extends BackboneElement { + mode?: ("match" | "include" | "outcome"); + score?: number; +} + +export interface BundleLink extends BackboneElement { + relation: string; + url: string; +} + +// CanonicalURL: http://hl7.org/fhir/StructureDefinition/Bundle (pkg: hl7.fhir.r4.core#4.0.1) +export interface Bundle extends Resource { + resourceType: "Bundle"; + + entry?: BundleEntry[]; + identifier?: Identifier; + link?: BundleLink[]; + signature?: Signature; + timestamp?: string; + _timestamp?: Element; + total?: number; + _total?: Element; + type: ("document" | "message" | "transaction" | "transaction-response" | "batch" | "batch-response" | "history" | "searchset" | "collection"); + _type?: Element; +} +export const isBundle = (resource: unknown): resource is Bundle => { + return resource !== null && typeof resource === "object" && (resource as {resourceType: string}).resourceType === "Bundle"; +} +" +`; diff --git a/test/api/write-generator/typescript.test.ts b/test/api/write-generator/typescript.test.ts index 3c827a07..bae0f770 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -33,10 +33,6 @@ describe("TypeScript Writer Generator", async () => { expect(bundleTs).toContain("resource?: T"); expect(bundleTs).toMatchSnapshot(); }); - it("generates Bundle with generic entry type (nested generic propagation)", async () => { - const bundleTs = files["generated/types/hl7-fhir-r4-core/Bundle.ts"]; - expect(bundleTs).toMatchSnapshot(); - }); it("generates Bundle with generic entry type (nested generic propagation)", async () => { const bundleTs = files["generated/types/hl7-fhir-r4-core/Bundle.ts"]; expect(bundleTs).toContain("export interface Bundle"); From 8ace59c8a9701c82f0af66cd929ce8f484b759a4 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 29 Apr 2026 17:01:32 +0200 Subject: [PATCH 08/21] TypeSchema/TS: store generic params on NestedTypeSchema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `generic.params` to `NestedTypeSchema` (with `GenericParam = { name, constraint: TypeIdentifier }`). Populate during `mkTypeSchemaIndex` after `populateTypeFamily` — for each nested in URL-sorted order, collect contributions: introduce (field type is a typeFamily root) or passthrough (field type is a generic-bearing nested with already-populated `generic.params`). The TS writer reads `target.generic?.params` directly instead of threading a per-pass `nestedGenericParams` accumulator. `generateType` and `generateNestedTypes` lose their threading parameter and `generateType` no longer needs a return value. Behavior change: this fixes an order-dependency in the old writer where a nested type couldn't see its sibling nesteds' generic params (because they hadn't been generated yet during the sequential pass). With the IR populated up front, the writer sees the full picture. As a result, e.g. `BundleEntry` now passes through `BundleEntryResponse`'s generic, becoming `BundleEntry` with `resource?: TResource; response?: BundleEntryResponse;`. `Bundle` itself stays one-param. --- src/api/writer-generator/typescript/writer.ts | 48 ++++++------ src/typeschema/types.ts | 10 +++ src/typeschema/utils.ts | 75 +++++++++++++++++++ 3 files changed, 108 insertions(+), 25 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index 1994b1f3..edbe075f 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -42,16 +42,15 @@ export const resolveTsAssets = (fn: string) => { return Path.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "typescript", fn); }; -type GenericParam = { name: string; constraint: string }; +type WriterGenericParam = { name: string; constraint: string }; type Contribution = | { kind: "introduce"; fieldName: string; constraint: string } - | { kind: "passthrough"; fieldName: string; params: GenericParam[] }; + | { kind: "passthrough"; fieldName: string; params: WriterGenericParam[] }; const collectGenericContributions = ( tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedTypeSchema, - nestedGenericParams: Record, ): Contribution[] => { const contributions: Contribution[] = []; for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { @@ -60,8 +59,14 @@ const collectGenericContributions = ( const target = tsIndex.resolveType(field.type); if (!target) continue; if (isNestedTypeSchema(target)) { - const params = nestedGenericParams[target.identifier.name]; - if (params?.length) contributions.push({ kind: "passthrough", fieldName: tsName, params }); + const params = target.generic?.params; + if (params?.length) { + contributions.push({ + kind: "passthrough", + fieldName: tsName, + params: params.map((p) => ({ name: p.name, constraint: p.constraint.name })), + }); + } } else if (isSpecializationTypeSchema(target) && (target.typeFamily?.resources?.length ?? 0) > 0) { contributions.push({ kind: "introduce", fieldName: tsName, constraint: field.type.name }); } @@ -232,8 +237,7 @@ export class TypeScript extends Writer { tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedTypeSchema, isFamilyType?: (ref: TypeIdentifier) => boolean, - nestedGenericParams: Record = {}, - ): GenericParam[] { + ): void { let name: string; // Generic types: Reference, Coding, CodeableConcept const genericTypes = ["Reference", "Coding", "CodeableConcept"]; @@ -245,15 +249,14 @@ export class TypeScript extends Writer { } // Per-field generic contributions: either introduce a fresh param (typeFamily-rooted field type) - // or inherit a nested type's existing params (pass-through). - const contributions = isHardcodedGeneric - ? [] - : collectGenericContributions(tsIndex, schema, nestedGenericParams); + // or inherit a nested type's existing params (pass-through). Nested-type passthrough reads + // `target.generic.params` populated during index build. + const contributions = isHardcodedGeneric ? [] : collectGenericContributions(tsIndex, schema); // Render contributions into paramList + per-field substitution maps. const fieldMap: Record = {}; const nestedArgsByField: Record = {}; - const paramList: GenericParam[] = []; + const paramList: WriterGenericParam[] = []; const introduceCount = contributions.filter((c) => c.kind === "introduce").length; const useShortName = introduceCount === 1 && contributions.length === 1; for (const c of contributions) { @@ -279,7 +282,7 @@ export class TypeScript extends Writer { this.debugComment(schema.identifier); if (!schema.fields && !extendsClause && !isResourceTypeSchema(schema)) { this.lineSM(`export type ${name} = object`); - return paramList; + return; } this.curlyBlock(["export", "interface", name, extendsClause], () => { if (isResourceTypeSchema(schema)) { @@ -326,7 +329,6 @@ export class TypeScript extends Writer { } } }); - return paramList; } withPrimitiveTypeExtension(schema: TypeSchema | NestedTypeSchema): boolean { @@ -353,16 +355,12 @@ export class TypeScript extends Writer { tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema, isFamilyType?: (ref: TypeIdentifier) => boolean, - ): Record { - const nestedGenericParams: Record = {}; - if (schema.nested) { - for (const subtype of schema.nested) { - const params = this.generateType(tsIndex, subtype, isFamilyType, nestedGenericParams); - nestedGenericParams[subtype.identifier.name] = params; - this.line(); - } + ): void { + if (!schema.nested) return; + for (const subtype of schema.nested) { + this.generateType(tsIndex, subtype, isFamilyType); + this.line(); } - return nestedGenericParams; } generateResourceModule(tsIndex: TypeSchemaIndex, schema: TypeSchema) { @@ -381,13 +379,13 @@ export class TypeScript extends Writer { this.generateDisclaimer(); this.generateDependenciesImports(tsIndex, schema); this.generateComplexTypeReexports(schema); - const nestedGenericParams = this.generateNestedTypes(tsIndex, schema, isFamilyType); + this.generateNestedTypes(tsIndex, schema, isFamilyType); this.comment( "CanonicalURL:", schema.identifier.url, `(pkg: ${packageMetaToFhir(packageMeta(schema))})`, ); - this.generateType(tsIndex, schema, isFamilyType, nestedGenericParams); + this.generateType(tsIndex, schema, isFamilyType); this.generateResourceTypePredicate(schema); }); } else { diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index cd0ccb3e..16f896f6 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -218,10 +218,20 @@ interface PrimitiveTypeSchema { dependencies?: TypeIdentifier[]; } +export type GenericParam = { + name: string; + constraint: TypeIdentifier; +}; + export interface NestedTypeSchema { identifier: NestedIdentifier; base: TypeIdentifier; fields: Record; + /** Generic params this nested type 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 nested-type field (passthrough). */ + generic?: { params: GenericParam[] }; } export interface ProfileTypeSchema { diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index 87c46dda..739a413e 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -10,6 +10,7 @@ import { type ComplexTypeTypeSchema, type ConstrainedChoiceInfo, type Field, + type GenericParam, type Identifier, isChoiceDeclarationField, isChoiceInstanceField, @@ -150,6 +151,78 @@ const populateTypeFamily = (schemas: TypeSchema[]): void => { } }; +/////////////////////////////////////////////////////////// +// Generic Params + +const upperFirst = (s: string): string => (s.length === 0 ? s : (s[0]?.toUpperCase() ?? "") + s.slice(1)); + +type GenericContribution = + | { kind: "introduce"; fieldName: string; constraint: TypeIdentifier } + | { kind: "passthrough"; fieldName: string; params: GenericParam[] }; + +const collectGenericContributions = ( + nested: NestedTypeSchema, + resolveType: (id: TypeIdentifier) => TypeSchema | NestedTypeSchema | undefined, +): GenericContribution[] => { + const contributions: GenericContribution[] = []; + for (const [fieldName, field] of Object.entries(nested.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) && (target.typeFamily?.resources?.length ?? 0) > 0) { + contributions.push({ kind: "introduce", fieldName, constraint: field.type }); + } + } + return contributions; +}; + +const renderGenericParams = (contributions: GenericContribution[]): GenericParam[] => { + const introduceCount = contributions.filter((c) => c.kind === "introduce").length; + const useShortName = introduceCount === 1 && contributions.length === 1; + const params: GenericParam[] = []; + for (const c of contributions) { + if (c.kind === "introduce") { + const name = useShortName ? "T" : `T${upperFirst(c.fieldName)}`; + params.push({ name, constraint: c.constraint }); + } else { + for (const np of c.params) { + if (!params.find((p) => p.name === np.name)) params.push(np); + } + } + } + return params; +}; + +/** Populate `generic.params` on each NestedTypeSchema. A nested becomes generic when one + * of its fields targets a type-family root (introduce) or a generic nested type + * (passthrough). Param naming policy: a single introduce with no passthrough → "T"; + * otherwise `T${UpperFirst(fieldName)}` per introduce, and passthrough names come from + * the target nested's already-populated params. Iterates nested schemas in URL-sorted + * order so passthrough lookups resolve in one pass. */ +const populateNestedGeneric = ( + schemas: TypeSchema[], + resolveType: (id: TypeIdentifier) => TypeSchema | NestedTypeSchema | undefined, +): void => { + // Clear stale data — schemas may be mutated by a previous index build (replaceSchemas). + for (const schema of schemas) { + if (!isSpecializationTypeSchema(schema) && !isProfileTypeSchema(schema)) continue; + for (const nested of schema.nested ?? []) nested.generic = undefined; + } + + for (const schema of schemas) { + if (!isSpecializationTypeSchema(schema) && !isProfileTypeSchema(schema)) continue; + if (!schema.nested) continue; + const sorted = [...schema.nested].sort((a, b) => a.identifier.url.localeCompare(b.identifier.url)); + for (const nested of sorted) { + const params = renderGenericParams(collectGenericContributions(nested, resolveType)); + if (params.length > 0) nested.generic = { params }; + } + } +}; + /////////////////////////////////////////////////////////// // Type Schema Index @@ -234,6 +307,8 @@ export const mkTypeSchemaIndex = ( if (isNestedIdentifier(id)) return nestedIndex[id.url]?.[id.package]; return index[id.url]?.[id.package]; }; + + populateNestedGeneric(schemas, resolveType); const resolveByUrl = (pkgName: PkgName, url: CanonicalUrl): TypeSchema | NestedTypeSchema | undefined => { if (register) { const resolutionTree = register.resolutionTree(); From bc085570972d0addc5507be6df9406b07861d8f9 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 29 Apr 2026 17:01:38 +0200 Subject: [PATCH 09/21] test: update snapshots and assertions for nested generic propagation --- .../__snapshots__/introspection.test.ts.snap | 30 ++- .../__snapshots__/typescript.test.ts.snap | 209 +----------------- test/api/write-generator/typescript.test.ts | 7 +- 3 files changed, 44 insertions(+), 202 deletions(-) diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index cba8107c..57b02ab9 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -1938,6 +1938,20 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "required": false, "excluded": false } + }, + "generic": { + "params": [ + { + "name": "T", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + } + } + ] } }, { @@ -2124,6 +2138,20 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "excluded": false, "array": false } + }, + "generic": { + "params": [ + { + "name": "T", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + } + } + ] } }, { @@ -2516,7 +2544,7 @@ exports[`IntrospectionWriter - TypeSchema output Check all introspection data in {"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":"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"}}]}},{"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"}}]}},{"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":"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 31962ed1..687c5cd3 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -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?: TResource; + 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; @@ -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?: TResource; + 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; @@ -2314,192 +2314,3 @@ export { USCoreTribalAffiliationExtensionProfile } from "./Extension_USCoreTriba export { USCoreVitalSignsProfile } from "./Observation_USCoreVitalSignsProfile"; " `; - -exports[`TypeScript Writer Generator generates Bundle with generic entry type (nested generic propagation) 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. - -import type { BackboneElement } from "../hl7-fhir-r4-core/BackboneElement"; -import type { Identifier } from "../hl7-fhir-r4-core/Identifier"; -import type { Resource } from "../hl7-fhir-r4-core/Resource"; -import type { Signature } from "../hl7-fhir-r4-core/Signature"; - -import type { Element } from "../hl7-fhir-r4-core/Element"; -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 { - fullUrl?: string; - link?: BundleLink[]; - request?: BundleEntryRequest; - resource?: T; - response?: BundleEntryResponse; - search?: BundleEntrySearch; -} - -export interface BundleEntryRequest extends BackboneElement { - ifMatch?: string; - ifModifiedSince?: string; - ifNoneExist?: string; - ifNoneMatch?: string; - method: ("GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH"); - url: string; -} - -export interface BundleEntryResponse extends BackboneElement { - etag?: string; - lastModified?: string; - location?: string; - outcome?: T; - status: string; -} - -export interface BundleEntrySearch extends BackboneElement { - mode?: ("match" | "include" | "outcome"); - score?: number; -} - -export interface BundleLink extends BackboneElement { - relation: string; - url: string; -} - -// CanonicalURL: http://hl7.org/fhir/StructureDefinition/Bundle (pkg: hl7.fhir.r4.core#4.0.1) -export interface Bundle extends Resource { - resourceType: "Bundle"; - - entry?: BundleEntry[]; - identifier?: Identifier; - link?: BundleLink[]; - signature?: Signature; - timestamp?: string; - _timestamp?: Element; - total?: number; - _total?: Element; - type: ("document" | "message" | "transaction" | "transaction-response" | "batch" | "batch-response" | "history" | "searchset" | "collection"); - _type?: Element; -} -export const isBundle = (resource: unknown): resource is Bundle => { - return resource !== null && typeof resource === "object" && (resource as {resourceType: string}).resourceType === "Bundle"; -} -" -`; - -exports[`TypeScript Writer Generator generates Coding with generic parameter 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. - -import type { Element } from "../hl7-fhir-r4-core/Element"; - -export type { Element } from "../hl7-fhir-r4-core/Element"; - -// CanonicalURL: http://hl7.org/fhir/StructureDefinition/Coding (pkg: hl7.fhir.r4.core#4.0.1) -export interface Coding extends Element { - code?: T; - _code?: Element; - display?: string; - _display?: Element; - system?: string; - _system?: Element; - userSelected?: boolean; - _userSelected?: Element; - version?: string; - _version?: Element; -} -" -`; - -exports[`TypeScript Writer Generator generates CodeableConcept with generic parameter 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. - -import type { Coding } from "../hl7-fhir-r4-core/Coding"; -import type { Element } from "../hl7-fhir-r4-core/Element"; - -export type { Coding } from "../hl7-fhir-r4-core/Coding"; -export type { Element } from "../hl7-fhir-r4-core/Element"; - -// CanonicalURL: http://hl7.org/fhir/StructureDefinition/CodeableConcept (pkg: hl7.fhir.r4.core#4.0.1) -export interface CodeableConcept extends Element { - coding?: Coding[]; - text?: string; - _text?: Element; -} -" -`; - -exports[`TypeScript Writer Generator generates BundleEntry with generic type-family parameter 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. - -import type { BackboneElement } from "../hl7-fhir-r4-core/BackboneElement"; -import type { Identifier } from "../hl7-fhir-r4-core/Identifier"; -import type { Resource } from "../hl7-fhir-r4-core/Resource"; -import type { Signature } from "../hl7-fhir-r4-core/Signature"; - -import type { Element } from "../hl7-fhir-r4-core/Element"; -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 { - fullUrl?: string; - link?: BundleLink[]; - request?: BundleEntryRequest; - resource?: T; - response?: BundleEntryResponse; - search?: BundleEntrySearch; -} - -export interface BundleEntryRequest extends BackboneElement { - ifMatch?: string; - ifModifiedSince?: string; - ifNoneExist?: string; - ifNoneMatch?: string; - method: ("GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH"); - url: string; -} - -export interface BundleEntryResponse extends BackboneElement { - etag?: string; - lastModified?: string; - location?: string; - outcome?: T; - status: string; -} - -export interface BundleEntrySearch extends BackboneElement { - mode?: ("match" | "include" | "outcome"); - score?: number; -} - -export interface BundleLink extends BackboneElement { - relation: string; - url: string; -} - -// CanonicalURL: http://hl7.org/fhir/StructureDefinition/Bundle (pkg: hl7.fhir.r4.core#4.0.1) -export interface Bundle extends Resource { - resourceType: "Bundle"; - - entry?: BundleEntry[]; - identifier?: Identifier; - link?: BundleLink[]; - signature?: Signature; - timestamp?: string; - _timestamp?: Element; - total?: number; - _total?: Element; - type: ("document" | "message" | "transaction" | "transaction-response" | "batch" | "batch-response" | "history" | "searchset" | "collection"); - _type?: Element; -} -export const isBundle = (resource: unknown): resource is Bundle => { - return resource !== null && typeof resource === "object" && (resource as {resourceType: string}).resourceType === "Bundle"; -} -" -`; diff --git a/test/api/write-generator/typescript.test.ts b/test/api/write-generator/typescript.test.ts index bae0f770..d8af74e3 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -29,8 +29,11 @@ describe("TypeScript Writer Generator", async () => { }); it("generates BundleEntry with generic type-family parameter", 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?: TResource"); + expect(bundleTs).toContain("response?: BundleEntryResponse"); expect(bundleTs).toMatchSnapshot(); }); it("generates Bundle with generic entry type (nested generic propagation)", async () => { From 94869527681964f73ea3965785ec29a8c238069d Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 10:18:54 +0200 Subject: [PATCH 10/21] TypeSchema/TS: name passthrough generic params by deep source field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `sourceField` to `GenericParam` IR type — the field that originally introduced the param (preserved through passthrough). Naming policy: - single param → "T" (short) - multiple params → `T${UpperFirst(sourceField)}` per param So a param introduced deep in `BundleEntryResponse.outcome` surfaces in `BundleEntry` as `TOutcome` rather than the local `T`, regardless of how many carrier hops it took to get there. Generator-side change: dedup raw params by `sourceField` (not by name). Writer mirrors the populator's logic for top-level schemas. Populator now iterates to a fixpoint instead of one URL-sorted pass — passthrough between siblings means earlier-processed nesteds don't see later siblings' introduces. Without fixpoint, BundleEntry's IR-stored `generic.params` would be stale (one param, missing the response passthrough), causing Bundle (top-level) to emit wrong arity. Effect on emit: Before: BundleEntry with response: BundleEntryResponse After: BundleEntry with response: BundleEntryResponse Bundle now exposes both: Bundle with entry: BundleEntry[] --- src/api/writer-generator/typescript/writer.ts | 49 +++++++++++--- src/typeschema/types.ts | 5 ++ src/typeschema/utils.ts | 67 ++++++++++++++----- 3 files changed, 94 insertions(+), 27 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index edbe075f..e7e8364c 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -42,7 +42,7 @@ export const resolveTsAssets = (fn: string) => { return Path.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "typescript", fn); }; -type WriterGenericParam = { name: string; constraint: string }; +type WriterGenericParam = { name: string; constraint: string; sourceField: string }; type Contribution = | { kind: "introduce"; fieldName: string; constraint: string } @@ -64,7 +64,11 @@ const collectGenericContributions = ( contributions.push({ kind: "passthrough", fieldName: tsName, - params: params.map((p) => ({ name: p.name, constraint: p.constraint.name })), + params: params.map((p) => ({ + name: p.name, + constraint: p.constraint.name, + sourceField: p.sourceField, + })), }); } } else if (isSpecializationTypeSchema(target) && (target.typeFamily?.resources?.length ?? 0) > 0) { @@ -253,22 +257,45 @@ export class TypeScript extends Writer { // `target.generic.params` populated during index build. const contributions = isHardcodedGeneric ? [] : collectGenericContributions(tsIndex, schema); - // Render contributions into paramList + per-field substitution maps. + // Render contributions into paramList + per-field substitution maps. Naming: + // single param → "T"; multiple → "T${UpperFirst(sourceField)}" per param, + // where sourceField is the deep field that originally introduced the param + // (preserved through passthrough so e.g. `outcome` shows up as `TOutcome` even + // when surfaced through an intermediate carrier). const fieldMap: Record = {}; const nestedArgsByField: Record = {}; - const paramList: WriterGenericParam[] = []; - const introduceCount = contributions.filter((c) => c.kind === "introduce").length; - const useShortName = introduceCount === 1 && contributions.length === 1; + type Raw = { sourceField: string; constraint: string; carrierField?: string }; + const raw: Raw[] = []; for (const c of contributions) { if (c.kind === "introduce") { - const paramName = useShortName ? "T" : `T${uppercaseFirstLetter(c.fieldName)}`; - fieldMap[c.fieldName] = paramName; - paramList.push({ name: paramName, constraint: c.constraint }); + if (!raw.find((r) => r.sourceField === c.fieldName)) { + raw.push({ sourceField: c.fieldName, constraint: c.constraint }); + } } else { for (const np of c.params) { - if (!paramList.find((p) => p.name === np.name)) paramList.push(np); + if (!raw.find((r) => r.sourceField === np.sourceField)) { + raw.push({ sourceField: np.sourceField, constraint: np.constraint, carrierField: c.fieldName }); + } } - nestedArgsByField[c.fieldName] = `<${c.params.map((p) => p.name).join(", ")}>`; + } + } + const useShortName = raw.length === 1; + const paramList: WriterGenericParam[] = raw.map((r) => ({ + name: useShortName ? "T" : `T${uppercaseFirstLetter(r.sourceField)}`, + constraint: r.constraint, + sourceField: r.sourceField, + })); + // Build per-field substitutions: introduces map fieldName→paramName; passthroughs + // collect their args by carrier field. + for (const c of contributions) { + if (c.kind === "introduce") { + const p = paramList.find((q) => q.sourceField === c.fieldName); + if (p) fieldMap[c.fieldName] = p.name; + } else { + const args = c.params.map( + (cp) => paramList.find((q) => q.sourceField === cp.sourceField)?.name ?? cp.name, + ); + nestedArgsByField[c.fieldName] = `<${args.join(", ")}>`; } } if (!isHardcodedGeneric && paramList.length > 0) { diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index 16f896f6..e620795f 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -221,6 +221,11 @@ interface PrimitiveTypeSchema { export type GenericParam = { name: string; constraint: TypeIdentifier; + /** The deep field that originally introduced this param (the typeFamily-rooted field + * at the bottom of the passthrough chain). Used for stable naming when the param + * surfaces in a parent schema (e.g. inherited from BundleEntryResponse.outcome → + * parent renames to `TOutcome` rather than the local `T`). */ + sourceField: string; }; export interface NestedTypeSchema { diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index 739a413e..addd3c29 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -180,28 +180,38 @@ const collectGenericContributions = ( }; const renderGenericParams = (contributions: GenericContribution[]): GenericParam[] => { - const introduceCount = contributions.filter((c) => c.kind === "introduce").length; - const useShortName = introduceCount === 1 && contributions.length === 1; - const params: GenericParam[] = []; + // Collect raw {sourceField, constraint} pairs across all contributions, deduped by sourceField. + type Raw = { sourceField: string; constraint: TypeIdentifier }; + const raw: Raw[] = []; for (const c of contributions) { if (c.kind === "introduce") { - const name = useShortName ? "T" : `T${upperFirst(c.fieldName)}`; - params.push({ name, constraint: c.constraint }); + if (!raw.find((r) => r.sourceField === c.fieldName)) { + raw.push({ sourceField: c.fieldName, constraint: c.constraint }); + } } else { for (const np of c.params) { - if (!params.find((p) => p.name === np.name)) params.push(np); + if (!raw.find((r) => r.sourceField === np.sourceField)) { + raw.push({ sourceField: np.sourceField, constraint: np.constraint }); + } } } } - return params; + if (raw.length === 0) return []; + // Single param → short name "T"; otherwise long names based on origin field. + const useShortName = raw.length === 1; + return raw.map((r) => ({ + name: useShortName ? "T" : `T${upperFirst(r.sourceField)}`, + constraint: r.constraint, + sourceField: r.sourceField, + })); }; /** Populate `generic.params` on each NestedTypeSchema. A nested becomes generic when one * of its fields targets a type-family root (introduce) or a generic nested type - * (passthrough). Param naming policy: a single introduce with no passthrough → "T"; - * otherwise `T${UpperFirst(fieldName)}` per introduce, and passthrough names come from - * the target nested's already-populated params. Iterates nested schemas in URL-sorted - * order so passthrough lookups resolve in one pass. */ + * (passthrough). Param naming policy: a single param → "T"; multiple params → + * `T${UpperFirst(sourceField)}` for each, where sourceField is the deep field that + * originally introduced the param (preserved through passthrough). Iterates nested + * schemas in URL-sorted order so passthrough lookups resolve in one pass. */ const populateNestedGeneric = ( schemas: TypeSchema[], resolveType: (id: TypeIdentifier) => TypeSchema | NestedTypeSchema | undefined, @@ -212,13 +222,38 @@ const populateNestedGeneric = ( for (const nested of schema.nested ?? []) nested.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.name !== y.name || x.sourceField !== y.sourceField || x.constraint.url !== y.constraint.url) + return false; + } + return true; + }; + + // Fixpoint: passthrough between siblings means later-processed nesteds influence earlier ones. + // Iterate until no changes; capped by total nested count as a safety bound. + let totalNested = 0; for (const schema of schemas) { if (!isSpecializationTypeSchema(schema) && !isProfileTypeSchema(schema)) continue; - if (!schema.nested) continue; - const sorted = [...schema.nested].sort((a, b) => a.identifier.url.localeCompare(b.identifier.url)); - for (const nested of sorted) { - const params = renderGenericParams(collectGenericContributions(nested, resolveType)); - if (params.length > 0) nested.generic = { params }; + totalNested += schema.nested?.length ?? 0; + } + let changed = true; + let iter = 0; + while (changed && iter++ < totalNested + 1) { + changed = false; + for (const schema of schemas) { + if (!isSpecializationTypeSchema(schema) && !isProfileTypeSchema(schema)) continue; + if (!schema.nested) continue; + for (const nested of schema.nested) { + const newParams = renderGenericParams(collectGenericContributions(nested, resolveType)); + const oldParams = nested.generic?.params ?? []; + if (sameParams(oldParams, newParams)) continue; + nested.generic = newParams.length > 0 ? { params: newParams } : undefined; + changed = true; + } } } }; From 96f46e76ad033c70be6bf0a43d735c4d9e67b9dc Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 10:18:58 +0200 Subject: [PATCH 11/21] test: update snapshots for sourceField-based generic naming --- .../__snapshots__/introspection.test.ts.snap | 21 +++++++++++++++---- .../__snapshots__/typescript.test.ts.snap | 20 +++++++++--------- test/api/write-generator/typescript.test.ts | 14 +++++++------ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index 57b02ab9..e2d3f93d 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -1942,14 +1942,26 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "generic": { "params": [ { - "name": "T", + "name": "TResource", "constraint": { "kind": "resource", "package": "hl7.fhir.r4.core", "version": "4.0.1", "name": "Resource", "url": "http://hl7.org/fhir/StructureDefinition/Resource" - } + }, + "sourceField": "resource" + }, + { + "name": "TOutcome", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + }, + "sourceField": "outcome" } ] } @@ -2149,7 +2161,8 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "version": "4.0.1", "name": "Resource", "url": "http://hl7.org/fhir/StructureDefinition/Resource" - } + }, + "sourceField": "outcome" } ] } @@ -2544,7 +2557,7 @@ exports[`IntrospectionWriter - TypeSchema output Check all introspection data in {"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":"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}},"generic":{"params":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"}}]}},{"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"}}]}},{"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":[{"name":"TResource","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"resource"},{"name":"TOutcome","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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"}]} {"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 687c5cd3..fb4f8f02 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?: TResource; - response?: BundleEntryResponse; + 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?: TResource; - response?: BundleEntryResponse; + 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/typescript.test.ts b/test/api/write-generator/typescript.test.ts index d8af74e3..829ffbaf 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -27,19 +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", + "export interface BundleEntry", ); expect(bundleTs).toContain("resource?: TResource"); - expect(bundleTs).toContain("response?: BundleEntryResponse"); + 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).toContain( + "export interface Bundle", + ); + expect(bundleTs).toContain("entry?: BundleEntry[]"); expect(bundleTs).toMatchSnapshot(); }); it("generates BundleEntryResponse with generic type-family parameter", async () => { From c94847f45a4b57e72f43a0b633dd6ab17cabcf54 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 10:49:28 +0200 Subject: [PATCH 12/21] TypeSchema/TS: switch generic param naming to T1/T2/... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use positional names (T1, T2, …) for multi-param schemas instead of the sourceField-derived TResource/TOutcome. Single-param schemas still emit "T". The IR-stored sourceField is preserved (still used to align passthrough args across nesting hops), only the rendered name changes. Effect on emit: Before: BundleEntry; entry: BundleEntry[] After: BundleEntry; entry: BundleEntry[] --- src/api/writer-generator/typescript/writer.ts | 6 ++---- src/typeschema/utils.ts | 9 +++------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index e7e8364c..775782be 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -1,6 +1,5 @@ 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, @@ -279,9 +278,8 @@ export class TypeScript extends Writer { } } } - const useShortName = raw.length === 1; - const paramList: WriterGenericParam[] = raw.map((r) => ({ - name: useShortName ? "T" : `T${uppercaseFirstLetter(r.sourceField)}`, + const paramList: WriterGenericParam[] = raw.map((r, i) => ({ + name: raw.length === 1 ? "T" : `T${i + 1}`, constraint: r.constraint, sourceField: r.sourceField, })); diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index addd3c29..4d8c3764 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -154,8 +154,6 @@ const populateTypeFamily = (schemas: TypeSchema[]): void => { /////////////////////////////////////////////////////////// // Generic Params -const upperFirst = (s: string): string => (s.length === 0 ? s : (s[0]?.toUpperCase() ?? "") + s.slice(1)); - type GenericContribution = | { kind: "introduce"; fieldName: string; constraint: TypeIdentifier } | { kind: "passthrough"; fieldName: string; params: GenericParam[] }; @@ -197,10 +195,9 @@ const renderGenericParams = (contributions: GenericContribution[]): GenericParam } } if (raw.length === 0) return []; - // Single param → short name "T"; otherwise long names based on origin field. - const useShortName = raw.length === 1; - return raw.map((r) => ({ - name: useShortName ? "T" : `T${upperFirst(r.sourceField)}`, + // Single param → "T"; multiple → "T1", "T2", … positional names. + return raw.map((r, i) => ({ + name: raw.length === 1 ? "T" : `T${i + 1}`, constraint: r.constraint, sourceField: r.sourceField, })); From 69a111f48e642fd18b67bf915afc6fc34a9302f5 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 10:49:33 +0200 Subject: [PATCH 13/21] test: update snapshots for T1/T2 generic naming --- .../__snapshots__/introspection.test.ts.snap | 6 +++--- .../__snapshots__/typescript.test.ts.snap | 20 +++++++++---------- test/api/write-generator/typescript.test.ts | 10 +++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index e2d3f93d..503686a7 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -1942,7 +1942,7 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "generic": { "params": [ { - "name": "TResource", + "name": "T1", "constraint": { "kind": "resource", "package": "hl7.fhir.r4.core", @@ -1953,7 +1953,7 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "sourceField": "resource" }, { - "name": "TOutcome", + "name": "T2", "constraint": { "kind": "resource", "package": "hl7.fhir.r4.core", @@ -2557,7 +2557,7 @@ exports[`IntrospectionWriter - TypeSchema output Check all introspection data in {"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":"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}},"generic":{"params":[{"name":"TResource","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"resource"},{"name":"TOutcome","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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"}]} +{"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":[{"name":"T1","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"resource"},{"name":"T2","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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"}]} {"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 fb4f8f02..d6aec051 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -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?: TResource; - 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; @@ -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?: TResource; - 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/typescript.test.ts b/test/api/write-generator/typescript.test.ts index 829ffbaf..cd51945e 100644 --- a/test/api/write-generator/typescript.test.ts +++ b/test/api/write-generator/typescript.test.ts @@ -30,18 +30,18 @@ describe("TypeScript Writer Generator", 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", + "export interface BundleEntry", ); - expect(bundleTs).toContain("resource?: TResource"); - expect(bundleTs).toContain("response?: BundleEntryResponse"); + expect(bundleTs).toContain("resource?: T1"); + expect(bundleTs).toContain("response?: BundleEntryResponse"); expect(bundleTs).toMatchSnapshot(); }); 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", + "export interface Bundle", ); - expect(bundleTs).toContain("entry?: BundleEntry[]"); + expect(bundleTs).toContain("entry?: BundleEntry[]"); expect(bundleTs).toMatchSnapshot(); }); it("generates BundleEntryResponse with generic type-family parameter", async () => { From c54b9ae01405eef9c746f531181b4feea73d5892 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 11:46:22 +0200 Subject: [PATCH 14/21] TypeSchema/TS: unify generic info on all specialization schemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Define `GenericInfo = { params: GenericParam[] }` once and use it on both `SpecializationTypeSchemaBody` (Resource, ComplexType, Logical) and `NestedTypeSchema`. Symmetric IR surface — any generic-bearing schema carries its params, not just nested ones. - Populator (`populateGeneric`, renamed from `populateNestedGeneric`) iterates over a single carrier list of top-level specializations + their nested in fixpoint. Profiles are intentionally out of scope — only their nested types participate. - `collectGenericContributions` now treats top-level schemas symmetrically with nested: a field whose target carries `generic.params` becomes a passthrough regardless of whether the target is top-level or nested. - TS writer's `generateType` reads `schema.generic?.params` directly (no local recomputation of param names). Per-field substitution maps still come from the contributions list, aligned to schema params by `sourceField`. Hardcoded TS specials (`Reference`, `Coding`, `CodeableConcept`) remain in the writer — their constraint is a primitive (`string`) and the rendering (template-literal Reference type, enum narrowing per field) is TS-flavored, not language-neutral IR concerns. Effect on emit: schemas like `DomainResource` and CDA's `ClinicalDocument` now also carry their populator-computed generics through to the writer (rather than being recomputed locally), making the typeFamily-based generic story fully data-driven from the IR. --- src/api/writer-generator/typescript/writer.ts | 86 +++++++------------ src/typeschema/types.ts | 18 ++-- src/typeschema/utils.ts | 72 +++++++++------- 3 files changed, 82 insertions(+), 94 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index 775782be..efe473f0 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -57,19 +57,18 @@ const collectGenericContributions = ( const tsName = tsFieldName(fieldName); const target = tsIndex.resolveType(field.type); if (!target) continue; - if (isNestedTypeSchema(target)) { - const params = target.generic?.params; - if (params?.length) { - contributions.push({ - kind: "passthrough", - fieldName: tsName, - params: params.map((p) => ({ - name: p.name, - constraint: p.constraint.name, - sourceField: p.sourceField, - })), - }); - } + const targetParams = + isNestedTypeSchema(target) || isSpecializationTypeSchema(target) ? target.generic?.params : undefined; + if (targetParams?.length) { + contributions.push({ + kind: "passthrough", + fieldName: tsName, + params: targetParams.map((p) => ({ + name: p.name, + constraint: p.constraint.name, + sourceField: p.sourceField, + })), + }); } else if (isSpecializationTypeSchema(target) && (target.typeFamily?.resources?.length ?? 0) > 0) { contributions.push({ kind: "introduce", fieldName: tsName, constraint: field.type.name }); } @@ -251,53 +250,32 @@ export class TypeScript extends Writer { name = tsResourceName(schema.identifier); } - // Per-field generic contributions: either introduce a fresh param (typeFamily-rooted field type) - // or inherit a nested type's existing params (pass-through). Nested-type passthrough reads - // `target.generic.params` populated during index build. - const contributions = isHardcodedGeneric ? [] : collectGenericContributions(tsIndex, schema); + // 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 ?? []); - // Render contributions into paramList + per-field substitution maps. Naming: - // single param → "T"; multiple → "T${UpperFirst(sourceField)}" per param, - // where sourceField is the deep field that originally introduced the param - // (preserved through passthrough so e.g. `outcome` shows up as `TOutcome` even - // when surfaced through an intermediate carrier). + // Per-field substitutions: when a field maps to one of the schema's params (introduce case), + // emit the param name in place of the field's type. When the field's target carries its own + // generic params (passthrough case), append matching args at the reference site, aligning + // by `sourceField` so deep origins line up across nesting hops. const fieldMap: Record = {}; const nestedArgsByField: Record = {}; - type Raw = { sourceField: string; constraint: string; carrierField?: string }; - const raw: Raw[] = []; - for (const c of contributions) { - if (c.kind === "introduce") { - if (!raw.find((r) => r.sourceField === c.fieldName)) { - raw.push({ sourceField: c.fieldName, constraint: c.constraint }); - } - } else { - for (const np of c.params) { - if (!raw.find((r) => r.sourceField === np.sourceField)) { - raw.push({ sourceField: np.sourceField, constraint: np.constraint, carrierField: c.fieldName }); - } + if (!isHardcodedGeneric) { + const contributions = collectGenericContributions(tsIndex, schema); + for (const c of contributions) { + if (c.kind === "introduce") { + const p = params.find((q) => q.sourceField === c.fieldName); + if (p) fieldMap[c.fieldName] = p.name; + } else { + const args = c.params.map( + (cp) => params.find((q) => q.sourceField === cp.sourceField)?.name ?? cp.name, + ); + nestedArgsByField[c.fieldName] = `<${args.join(", ")}>`; } } } - const paramList: WriterGenericParam[] = raw.map((r, i) => ({ - name: raw.length === 1 ? "T" : `T${i + 1}`, - constraint: r.constraint, - sourceField: r.sourceField, - })); - // Build per-field substitutions: introduces map fieldName→paramName; passthroughs - // collect their args by carrier field. - for (const c of contributions) { - if (c.kind === "introduce") { - const p = paramList.find((q) => q.sourceField === c.fieldName); - if (p) fieldMap[c.fieldName] = p.name; - } else { - const args = c.params.map( - (cp) => paramList.find((q) => q.sourceField === cp.sourceField)?.name ?? cp.name, - ); - nestedArgsByField[c.fieldName] = `<${args.join(", ")}>`; - } - } - if (!isHardcodedGeneric && paramList.length > 0) { - const declParams = paramList.map((p) => `${p.name} extends ${p.constraint} = ${p.constraint}`); + if (!isHardcodedGeneric && params.length > 0) { + const declParams = params.map((p) => `${p.name} extends ${p.constraint.name} = ${p.constraint.name}`); name += `<${declParams.join(", ")}>`; } diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index e620795f..b42ce381 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -223,20 +223,23 @@ export type GenericParam = { constraint: TypeIdentifier; /** The deep field that originally introduced this param (the typeFamily-rooted field * at the bottom of the passthrough chain). Used for stable naming when the param - * surfaces in a parent schema (e.g. inherited from BundleEntryResponse.outcome → - * parent renames to `TOutcome` rather than the local `T`). */ + * surfaces in a parent schema and to align passthrough args across nesting hops. */ sourceField: 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 params this nested type 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 nested-type field (passthrough). */ - generic?: { params: GenericParam[] }; + generic?: GenericInfo; } export interface ProfileTypeSchema { @@ -314,6 +317,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 4d8c3764..b44dec69 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -159,19 +159,24 @@ type GenericContribution = | { kind: "passthrough"; fieldName: string; params: GenericParam[] }; const collectGenericContributions = ( - nested: NestedTypeSchema, + schema: SpecializationTypeSchema | NestedTypeSchema, resolveType: (id: TypeIdentifier) => TypeSchema | NestedTypeSchema | undefined, ): GenericContribution[] => { const contributions: GenericContribution[] = []; - for (const [fieldName, field] of Object.entries(nested.fields ?? {})) { + 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) && (target.typeFamily?.resources?.length ?? 0) > 0) { - contributions.push({ kind: "introduce", fieldName, constraint: field.type }); + } 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; @@ -203,22 +208,32 @@ const renderGenericParams = (contributions: GenericContribution[]): GenericParam })); }; -/** Populate `generic.params` on each NestedTypeSchema. A nested becomes generic when one - * of its fields targets a type-family root (introduce) or a generic nested type - * (passthrough). Param naming policy: a single param → "T"; multiple params → - * `T${UpperFirst(sourceField)}` for each, where sourceField is the deep field that - * originally introduced the param (preserved through passthrough). Iterates nested - * schemas in URL-sorted order so passthrough lookups resolve in one pass. */ -const populateNestedGeneric = ( +/** 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 => { - // Clear stale data — schemas may be mutated by a previous index build (replaceSchemas). + type Carrier = SpecializationTypeSchema | NestedTypeSchema; + const carriers: Carrier[] = []; for (const schema of schemas) { - if (!isSpecializationTypeSchema(schema) && !isProfileTypeSchema(schema)) continue; - for (const nested of schema.nested ?? []) nested.generic = undefined; + 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++) { @@ -230,27 +245,18 @@ const populateNestedGeneric = ( return true; }; - // Fixpoint: passthrough between siblings means later-processed nesteds influence earlier ones. - // Iterate until no changes; capped by total nested count as a safety bound. - let totalNested = 0; - for (const schema of schemas) { - if (!isSpecializationTypeSchema(schema) && !isProfileTypeSchema(schema)) continue; - totalNested += schema.nested?.length ?? 0; - } + // 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++ < totalNested + 1) { + while (changed && iter++ <= carriers.length) { changed = false; - for (const schema of schemas) { - if (!isSpecializationTypeSchema(schema) && !isProfileTypeSchema(schema)) continue; - if (!schema.nested) continue; - for (const nested of schema.nested) { - const newParams = renderGenericParams(collectGenericContributions(nested, resolveType)); - const oldParams = nested.generic?.params ?? []; - if (sameParams(oldParams, newParams)) continue; - nested.generic = newParams.length > 0 ? { params: newParams } : undefined; - changed = true; - } + 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; } } }; @@ -340,7 +346,7 @@ export const mkTypeSchemaIndex = ( return index[id.url]?.[id.package]; }; - populateNestedGeneric(schemas, resolveType); + populateGeneric(schemas, resolveType); const resolveByUrl = (pkgName: PkgName, url: CanonicalUrl): TypeSchema | NestedTypeSchema | undefined => { if (register) { const resolutionTree = register.resolutionTree(); From 0aa42c1e90c0cac8ef17d7c2a5e8a7a2d1c21b69 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 11:46:27 +0200 Subject: [PATCH 15/21] test: update CDA + introspection snapshots for unified generic IR --- .../__snapshots__/introspection.test.ts.snap | 32 +++++++++++++++++-- .../__snapshots__/cda.test.ts.snap | 24 +++++++------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index 503686a7..90bbaf3b 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -2360,7 +2360,33 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "name": "SearchEntryMode", "url": "urn:fhir:binding:SearchEntryMode" } - ] + ], + "generic": { + "params": [ + { + "name": "T1", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + }, + "sourceField": "resource" + }, + { + "name": "T2", + "constraint": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Resource", + "url": "http://hl7.org/fhir/StructureDefinition/Resource" + }, + "sourceField": "outcome" + } + ] + } }" `; @@ -2554,10 +2580,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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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}},"generic":{"params":[{"name":"T1","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"resource"},{"name":"T2","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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"}]} +{"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":[{"name":"T1","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"resource"},{"name":"T2","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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":[{"name":"T1","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"resource"},{"name":"T2","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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/multi-package/__snapshots__/cda.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/cda.test.ts.snap index 8cba7c86..57a9ed47 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 @@ -31,32 +31,32 @@ import type { TS } from "../hl7-cda-uv-core/TS"; import type { Element } from "../hl7-fhir-r5-core/Element"; // 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; From dd8e4064c22a9af70657b2065eca5f7351f93c51 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 12:56:41 +0200 Subject: [PATCH 16/21] =?UTF-8?q?TypeSchema/TS:=20rename=20GenericParam.{n?= =?UTF-8?q?ame,sourceField}=20=E2=86=92=20{typeVar,path}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `path` is now `string[]` carrying the full origin trail from the schema down to the typeFamily-rooted field that introduces the param. Single-segment for direct introduce (e.g. `["outcome"]`), grown by passthrough as the param surfaces through carrier fields (e.g. `["response", "outcome"]`, then `["entry", "response", "outcome"]`). Dedup is by leaf segment of the path — multiple fields with the same deepest origin still share one generic param (so callers narrow once to update many fields). Param identity stays the same as before; only the IR data is richer. Naming policy unchanged: single param → "T", multiple → positional T1/T2/…. Writer alignment also matches by leaf segment of `path` (with fallback to the child param's typeVar) so passthrough args render correctly across nesting hops. --- src/api/writer-generator/typescript/writer.ts | 22 +++++++++------- src/typeschema/types.ts | 12 +++++---- src/typeschema/utils.ts | 26 ++++++++++++------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index efe473f0..5783f942 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -41,12 +41,14 @@ export const resolveTsAssets = (fn: string) => { return Path.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "typescript", fn); }; -type WriterGenericParam = { name: string; constraint: string; sourceField: string }; +type WriterGenericParam = { typeVar: string; constraint: string; path: string[] }; type Contribution = | { kind: "introduce"; fieldName: string; constraint: string } | { kind: "passthrough"; fieldName: string; params: WriterGenericParam[] }; +const leafOf = (path: string[]): string => path[path.length - 1] ?? ""; + const collectGenericContributions = ( tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedTypeSchema, @@ -64,9 +66,9 @@ const collectGenericContributions = ( kind: "passthrough", fieldName: tsName, params: targetParams.map((p) => ({ - name: p.name, + typeVar: p.typeVar, constraint: p.constraint.name, - sourceField: p.sourceField, + path: p.path, })), }); } else if (isSpecializationTypeSchema(target) && (target.typeFamily?.resources?.length ?? 0) > 0) { @@ -255,27 +257,27 @@ export class TypeScript extends Writer { const params = isHardcodedGeneric ? [] : (schema.generic?.params ?? []); // Per-field substitutions: when a field maps to one of the schema's params (introduce case), - // emit the param name in place of the field's type. When the field's target carries its own - // generic params (passthrough case), append matching args at the reference site, aligning - // by `sourceField` so deep origins line up across nesting hops. + // emit the param's typeVar in place of the field's type. When the field's target carries its + // own generic params (passthrough case), append matching args at the reference site, aligning + // by leaf segment of the param's `path` (the deepest origin). const fieldMap: Record = {}; const nestedArgsByField: Record = {}; if (!isHardcodedGeneric) { const contributions = collectGenericContributions(tsIndex, schema); for (const c of contributions) { if (c.kind === "introduce") { - const p = params.find((q) => q.sourceField === c.fieldName); - if (p) fieldMap[c.fieldName] = p.name; + const p = params.find((q) => leafOf(q.path) === c.fieldName); + if (p) fieldMap[c.fieldName] = p.typeVar; } else { const args = c.params.map( - (cp) => params.find((q) => q.sourceField === cp.sourceField)?.name ?? cp.name, + (cp) => params.find((q) => leafOf(q.path) === leafOf(cp.path))?.typeVar ?? cp.typeVar, ); nestedArgsByField[c.fieldName] = `<${args.join(", ")}>`; } } } if (!isHardcodedGeneric && params.length > 0) { - const declParams = params.map((p) => `${p.name} extends ${p.constraint.name} = ${p.constraint.name}`); + const declParams = params.map((p) => `${p.typeVar} extends ${p.constraint.name} = ${p.constraint.name}`); name += `<${declParams.join(", ")}>`; } diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index b42ce381..ea99121d 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -219,12 +219,14 @@ interface PrimitiveTypeSchema { } export type GenericParam = { - name: string; + typeVar: string; constraint: TypeIdentifier; - /** The deep field that originally introduced this param (the typeFamily-rooted field - * at the bottom of the passthrough chain). Used for stable naming when the param - * surfaces in a parent schema and to align passthrough args across nesting hops. */ - sourceField: string; + /** 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 diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index b44dec69..eb45bb28 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -182,19 +182,27 @@ const collectGenericContributions = ( 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 {sourceField, constraint} pairs across all contributions, deduped by sourceField. - type Raw = { sourceField: string; constraint: TypeIdentifier }; + // 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") { - if (!raw.find((r) => r.sourceField === c.fieldName)) { - raw.push({ sourceField: c.fieldName, constraint: c.constraint }); + 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) { - if (!raw.find((r) => r.sourceField === np.sourceField)) { - raw.push({ sourceField: np.sourceField, constraint: np.constraint }); + const path = [c.fieldName, ...np.path]; + if (!raw.find((r) => leafOf(r.path) === leafOf(path))) { + raw.push({ path, constraint: np.constraint }); } } } @@ -202,9 +210,9 @@ const renderGenericParams = (contributions: GenericContribution[]): GenericParam if (raw.length === 0) return []; // Single param → "T"; multiple → "T1", "T2", … positional names. return raw.map((r, i) => ({ - name: raw.length === 1 ? "T" : `T${i + 1}`, + typeVar: raw.length === 1 ? "T" : `T${i + 1}`, constraint: r.constraint, - sourceField: r.sourceField, + path: r.path, })); }; @@ -239,7 +247,7 @@ const populateGeneric = ( for (let i = 0; i < a.length; i++) { const x = a[i]!; const y = b[i]!; - if (x.name !== y.name || x.sourceField !== y.sourceField || x.constraint.url !== y.constraint.url) + if (x.typeVar !== y.typeVar || !samePath(x.path, y.path) || x.constraint.url !== y.constraint.url) return false; } return true; From e3307b72833a1ef84a1801c7b58c16ef69ac0717 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 12:56:46 +0200 Subject: [PATCH 17/21] test: update introspection snapshots for typeVar/path field rename --- .../__snapshots__/introspection.test.ts.snap | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index 90bbaf3b..bacdd839 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -1942,7 +1942,7 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "generic": { "params": [ { - "name": "T1", + "typeVar": "T1", "constraint": { "kind": "resource", "package": "hl7.fhir.r4.core", @@ -1950,10 +1950,12 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "name": "Resource", "url": "http://hl7.org/fhir/StructureDefinition/Resource" }, - "sourceField": "resource" + "path": [ + "resource" + ] }, { - "name": "T2", + "typeVar": "T2", "constraint": { "kind": "resource", "package": "hl7.fhir.r4.core", @@ -1961,7 +1963,10 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "name": "Resource", "url": "http://hl7.org/fhir/StructureDefinition/Resource" }, - "sourceField": "outcome" + "path": [ + "response", + "outcome" + ] } ] } @@ -2154,7 +2159,7 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "generic": { "params": [ { - "name": "T", + "typeVar": "T", "constraint": { "kind": "resource", "package": "hl7.fhir.r4.core", @@ -2162,7 +2167,9 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "name": "Resource", "url": "http://hl7.org/fhir/StructureDefinition/Resource" }, - "sourceField": "outcome" + "path": [ + "outcome" + ] } ] } @@ -2364,7 +2371,7 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "generic": { "params": [ { - "name": "T1", + "typeVar": "T1", "constraint": { "kind": "resource", "package": "hl7.fhir.r4.core", @@ -2372,10 +2379,13 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "name": "Resource", "url": "http://hl7.org/fhir/StructureDefinition/Resource" }, - "sourceField": "resource" + "path": [ + "entry", + "resource" + ] }, { - "name": "T2", + "typeVar": "T2", "constraint": { "kind": "resource", "package": "hl7.fhir.r4.core", @@ -2383,7 +2393,11 @@ exports[`IntrospectionWriter - TypeSchema output Check Bundle type schema 1`] = "name": "Resource", "url": "http://hl7.org/fhir/StructureDefinition/Resource" }, - "sourceField": "outcome" + "path": [ + "entry", + "response", + "outcome" + ] } ] } @@ -2580,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"}]},"generic":{"params":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"contained"}]}} +{"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}},"generic":{"params":[{"name":"T1","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"resource"},{"name":"T2","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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":[{"name":"T","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"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":[{"name":"T1","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"resource"},{"name":"T2","constraint":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"sourceField":"outcome"}]}} +{"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"}]} From 59a1c5acd1dbe7ec615bf030fcd55c5f3c93534d Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 14:11:52 +0200 Subject: [PATCH 18/21] TypeSchema/TS: import generic constraint types and skip hardcoded specials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes uncovered by `make all` cross-project type check: - Add each schema's generic-param constraint identifiers to its `dependencies` list, so the writer's import emission picks them up. Without this, schemas declared as `` in CDA were missing the `Base` import and failed type-check. - In the writer's `collectGenericContributions`, skip targets named `Reference`/`Coding`/`CodeableConcept` — these have hardcoded TS-specific generic declarations (``). When a non-FHIR-core schema reuses one of these names (e.g. CDA's `Reference`) and ends up with a populator-computed `generic.params` over a different constraint (Base), the IR-driven passthrough at reference sites would emit `` args clashing with the hardcoded `T extends string`. Skip them at reference sites; the hardcoded TS rendering handles their own declaration and field overrides. --- src/api/writer-generator/typescript/writer.ts | 6 ++++++ src/typeschema/utils.ts | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index 5783f942..5338fe9e 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -49,6 +49,11 @@ type Contribution = 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"]); + const collectGenericContributions = ( tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedTypeSchema, @@ -59,6 +64,7 @@ const collectGenericContributions = ( const tsName = tsFieldName(fieldName); const target = tsIndex.resolveType(field.type); if (!target) continue; + if (TS_HARDCODED_GENERIC_NAMES.has(target.identifier.name)) continue; const targetParams = isNestedTypeSchema(target) || isSpecializationTypeSchema(target) ? target.generic?.params : undefined; if (targetParams?.length) { diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index eb45bb28..a9e45eda 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -9,6 +9,7 @@ import { type ChoiceFieldInstance, type ComplexTypeTypeSchema, type ConstrainedChoiceInfo, + concatIdentifiers, type Field, type GenericParam, type Identifier, @@ -267,6 +268,25 @@ const populateGeneric = ( 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; + } }; /////////////////////////////////////////////////////////// From ae2551c48a2f8e6f00216423a0d64b7d4e0b4adf Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 14:11:56 +0200 Subject: [PATCH 19/21] =?UTF-8?q?test:=20update=20CDA=20snapshot=20?= =?UTF-8?q?=E2=80=94=20Base=20imported=20via=20generic=20constraint=20deps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../multi-package/__snapshots__/cda.test.ts.snap | 4 ++++ 1 file changed, 4 insertions(+) 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 57a9ed47..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,6 +31,8 @@ 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[]; @@ -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): From 34b7a5da9edb1f61d81bcfad2cea88f0f6700e1b Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 14:12:00 +0200 Subject: [PATCH 20/21] TS: regenerate R4 + US Core examples --- .../fhir-types/hl7-fhir-r4-core/Bundle.ts | 10 +++++----- .../profiles/Observation_observation_vitalsigns.ts | 7 +++++++ .../profiles/Extension_USCoreEthnicityExtension.ts | 5 +++++ .../profiles/Extension_USCoreIndividualSexExtension.ts | 5 +++++ .../Extension_USCoreInterpreterNeededExtension.ts | 5 +++++ .../profiles/Extension_USCoreRaceExtension.ts | 5 +++++ .../Extension_USCoreTribalAffiliationExtension.ts | 5 +++++ .../profiles/Observation_USCoreBloodPressureProfile.ts | 7 +++++++ .../profiles/Observation_USCoreBodyWeightProfile.ts | 7 +++++++ .../profiles/Observation_USCoreVitalSignsProfile.ts | 7 +++++++ .../profiles/Patient_USCorePatientProfile.ts | 7 +++++++ 11 files changed, 65 insertions(+), 5 deletions(-) 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 dfb13c8c..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-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); From 9f1df64f622318b8200dec32c184fada8c0f3528 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Fri, 1 May 2026 14:53:41 +0200 Subject: [PATCH 21/21] ref: inline per-field generic substitution in TS writer Drop the `Contribution`/`WriterGenericParam` intermediates and the `collectGenericContributions` helper. Walk fields once during emit and compute `fieldMap`/`nestedArgsByField` inline against the schema's IR-stored `generic.params`. Behaviorally identical (still aligns passthrough args by `leafOf(path)`, still skips hardcoded `Reference`/`Coding`/`CodeableConcept` at reference sites). Net -30 lines in the writer. --- src/api/writer-generator/typescript/writer.ts | 68 ++++++------------- 1 file changed, 19 insertions(+), 49 deletions(-) diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index 5338fe9e..885d4c92 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -41,12 +41,6 @@ export const resolveTsAssets = (fn: string) => { return Path.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "typescript", fn); }; -type WriterGenericParam = { typeVar: string; constraint: string; path: string[] }; - -type Contribution = - | { kind: "introduce"; fieldName: string; constraint: string } - | { kind: "passthrough"; fieldName: string; params: WriterGenericParam[] }; - const leafOf = (path: string[]): string => path[path.length - 1] ?? ""; // Schemas that the TS writer renders with a hardcoded `` generic — their IR @@ -54,36 +48,6 @@ const leafOf = (path: string[]): string => path[path.length - 1] ?? ""; // so we don't emit `` args clashing with the hardcoded `T extends string` declaration. const TS_HARDCODED_GENERIC_NAMES = new Set(["Reference", "Coding", "CodeableConcept"]); -const collectGenericContributions = ( - tsIndex: TypeSchemaIndex, - schema: SpecializationTypeSchema | NestedTypeSchema, -): Contribution[] => { - const contributions: Contribution[] = []; - for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { - if (isChoiceDeclarationField(field) || !field.type) continue; - const tsName = tsFieldName(fieldName); - const target = tsIndex.resolveType(field.type); - if (!target) continue; - if (TS_HARDCODED_GENERIC_NAMES.has(target.identifier.name)) continue; - const targetParams = - isNestedTypeSchema(target) || isSpecializationTypeSchema(target) ? target.generic?.params : undefined; - if (targetParams?.length) { - contributions.push({ - kind: "passthrough", - fieldName: tsName, - params: targetParams.map((p) => ({ - typeVar: p.typeVar, - constraint: p.constraint.name, - path: p.path, - })), - }); - } else if (isSpecializationTypeSchema(target) && (target.typeFamily?.resources?.length ?? 0) > 0) { - contributions.push({ kind: "introduce", fieldName: tsName, constraint: field.type.name }); - } - } - return contributions; -}; - export type TypeScriptOptions = { lineWidth?: number; /** openResourceTypeSet -- for resource families (Resource, DomainResource) use open set for resourceType field. @@ -262,23 +226,29 @@ export class TypeScript extends Writer { // Hardcoded TS specials (Reference/Coding/CodeableConcept) get their `` above. const params = isHardcodedGeneric ? [] : (schema.generic?.params ?? []); - // Per-field substitutions: when a field maps to one of the schema's params (introduce case), - // emit the param's typeVar in place of the field's type. When the field's target carries its - // own generic params (passthrough case), append matching args at the reference site, aligning - // by leaf segment of the param's `path` (the deepest origin). + // 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) { - const contributions = collectGenericContributions(tsIndex, schema); - for (const c of contributions) { - if (c.kind === "introduce") { - const p = params.find((q) => leafOf(q.path) === c.fieldName); - if (p) fieldMap[c.fieldName] = p.typeVar; - } else { - const args = c.params.map( - (cp) => params.find((q) => leafOf(q.path) === leafOf(cp.path))?.typeVar ?? cp.typeVar, + 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[c.fieldName] = `<${args.join(", ")}>`; + 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; } } }