From 3354f2b395bf79bfbae576356cb4a680de21bca5 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 6 Apr 2026 14:17:14 +0200 Subject: [PATCH 1/7] feat: Array setters/getters for unbounded (max: *) slice definitions For slices with max: * (unbounded cardinality), setters now accept arrays and getters return arrays. Slices with max: 1 keep the single-element API. --- .../typescript/profile-helpers.ts | 20 ++ .../typescript/profile-slices.ts | 182 ++++++++++++------ .../writer-generator/typescript/profile.ts | 2 + 3 files changed, 144 insertions(+), 60 deletions(-) diff --git a/assets/api/writer-generator/typescript/profile-helpers.ts b/assets/api/writer-generator/typescript/profile-helpers.ts index 239ec0d0..2c159297 100644 --- a/assets/api/writer-generator/typescript/profile-helpers.ts +++ b/assets/api/writer-generator/typescript/profile-helpers.ts @@ -296,6 +296,26 @@ export const getArraySlice = (list: readonly T[] | undefined, match: Record matchesValue(item, match)); }; +/** Return all elements in `list` that satisfy the slice discriminator `match`. */ +export const getArraySliceAll = (list: readonly T[] | undefined, match: Record): T[] => { + if (!list) return []; + return list.filter((item) => matchesValue(item, match)); +}; + +/** + * Replace all elements matching `match` in `list` with `newItems`. + * Each new item has the discriminator values applied via {@link applySliceMatch} + * before this call, so this helper only handles the array surgery. + */ +export const setArraySliceAll = (list: T[], match: Record, newItems: T[]): void => { + // Remove all existing items that match the discriminator + for (let i = list.length - 1; i >= 0; i--) { + if (matchesValue(list[i], match)) list.splice(i, 1); + } + // Append new items + list.push(...newItems); +}; + // --------------------------------------------------------------------------- // Validation helpers // diff --git a/src/api/writer-generator/typescript/profile-slices.ts b/src/api/writer-generator/typescript/profile-slices.ts index a56ec722..d5fc7e8c 100644 --- a/src/api/writer-generator/typescript/profile-slices.ts +++ b/src/api/writer-generator/typescript/profile-slices.ts @@ -109,6 +109,8 @@ export type SliceDef = { constrainedChoice: ConstrainedChoiceInfo | undefined; /** True when the slice uses a type discriminator (match by resourceType) */ typeDiscriminator: boolean; + /** Max cardinality of the slice. 0 or undefined = unbounded ("*"), positive = exact limit. */ + max: number; }; export const collectSliceDefs = (tsIndex: TypeSchemaIndex, flatProfile: ProfileTypeSchema): SliceDef[] => @@ -145,6 +147,7 @@ export const collectSliceDefs = (tsIndex: TypeSchemaIndex, flatProfile: ProfileT array: Boolean(field.array), constrainedChoice, typeDiscriminator: isTypeDisc, + max: slice.max ?? 0, }; }); }); @@ -165,35 +168,59 @@ export const generateSliceSetters = ( const tsField = tsFieldName(sliceDef.fieldName); const fieldAccess = tsGet("this.resource", tsField); const baseType = sliceDef.typedBaseType; - // Make input optional when there are no required fields (input can be empty object) - const inputOptional = sliceDef.required.length === 0; - const unionType = `${inputTypeName} | ${baseType}`; - const paramSignature = inputOptional ? `(input?: ${unionType}): this` : `(input: ${unionType}): this`; - w.curlyBlock(["public", methodName, paramSignature], () => { - w.line(`const match = ${matchRef}`); - w.curlyBlock(["if", "(input && matchesValue(input, match))"], () => { + const isUnbounded = sliceDef.array && (sliceDef.max === 0 || sliceDef.max === undefined); + + if (isUnbounded) { + // Unbounded slice: accept an array of items + const unionType = `(${inputTypeName} | ${baseType})[]`; + const paramSignature = `(input: ${unionType}): this`; + w.curlyBlock(["public", methodName, paramSignature], () => { + w.line(`const match = ${matchRef}`); + w.line(`const arr = ${fieldAccess} ??= []`); + if (sliceDef.constrainedChoice) { + const cc = sliceDef.constrainedChoice; + w.line( + `const values = input.map(item => matchesValue(item, match) ? item as ${baseType} : applySliceMatch<${baseType}>(wrapSliceChoice<${baseType}>(item, ${JSON.stringify(cc.variant)}), match))`, + ); + } else { + w.line( + `const values = input.map(item => matchesValue(item, match) ? item as ${baseType} : applySliceMatch<${baseType}>(item, match))`, + ); + } + w.line("setArraySliceAll(arr, match, values)"); + w.line("return this"); + }); + } else { + // Single-element slice (max: 1): keep existing behavior + const inputOptional = sliceDef.required.length === 0; + const unionType = `${inputTypeName} | ${baseType}`; + const paramSignature = inputOptional ? `(input?: ${unionType}): this` : `(input: ${unionType}): this`; + w.curlyBlock(["public", methodName, paramSignature], () => { + w.line(`const match = ${matchRef}`); + w.curlyBlock(["if", "(input && matchesValue(input, match))"], () => { + if (sliceDef.array) { + w.line(`setArraySlice(${fieldAccess} ??= [], match, input as ${baseType})`); + } else { + w.line(`${fieldAccess} = input as ${baseType}`); + } + w.line("return this"); + }); + const inputExpr = inputOptional ? "input ?? {}" : "input"; + if (sliceDef.constrainedChoice) { + const cc = sliceDef.constrainedChoice; + w.line(`const wrapped = wrapSliceChoice<${baseType}>(${inputExpr}, ${JSON.stringify(cc.variant)})`); + w.line(`const value = applySliceMatch<${baseType}>(wrapped, match)`); + } else { + w.line(`const value = applySliceMatch<${baseType}>(${inputExpr}, match)`); + } if (sliceDef.array) { - w.line(`setArraySlice(${fieldAccess} ??= [], match, input as ${baseType})`); + w.line(`setArraySlice(${fieldAccess} ??= [], match, value)`); } else { - w.line(`${fieldAccess} = input as ${baseType}`); + w.line(`${fieldAccess} = value`); } w.line("return this"); }); - const inputExpr = inputOptional ? "input ?? {}" : "input"; - if (sliceDef.constrainedChoice) { - const cc = sliceDef.constrainedChoice; - w.line(`const wrapped = wrapSliceChoice<${baseType}>(${inputExpr}, ${JSON.stringify(cc.variant)})`); - w.line(`const value = applySliceMatch<${baseType}>(wrapped, match)`); - } else { - w.line(`const value = applySliceMatch<${baseType}>(${inputExpr}, match)`); - } - if (sliceDef.array) { - w.line(`setArraySlice(${fieldAccess} ??= [], match, value)`); - } else { - w.line(`${fieldAccess} = value`); - } - w.line("return this"); - }); + } w.line(); } }; @@ -216,44 +243,79 @@ export const generateSliceGetters = ( const tsField = tsFieldName(sliceDef.fieldName); const fieldAccess = tsGet("this.resource", tsField); const baseType = sliceDef.typedBaseType; - const defaultReturn = defaultMode === "raw" ? baseType : flatTypeName; + const isUnbounded = sliceDef.array && (sliceDef.max === 0 || sliceDef.max === undefined); - // Overload signatures - w.lineSM(`public ${getMethodName}(mode: 'flat'): ${flatTypeName} | undefined`); - w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType} | undefined`); - w.lineSM(`public ${getMethodName}(): ${defaultReturn} | undefined`); + if (isUnbounded) { + // Unbounded slice: return an array + const defaultReturn = defaultMode === "raw" ? `${baseType}[]` : `${flatTypeName}[]`; - // Implementation - w.curlyBlock( - [ - "public", - getMethodName, - `(mode: 'flat' | 'raw' = '${defaultMode}'): ${flatTypeName} | ${baseType} | undefined`, - ], - () => { - w.line(`const match = ${matchRef}`); - if (sliceDef.array) { - w.line(`const item = getArraySlice(${fieldAccess}, match)`); - w.line("if (!item) return undefined"); - } else { - w.line(`const item = ${fieldAccess}`); - w.line("if (!item || !matchesValue(item, match)) return undefined"); - } - if (sliceDef.typeDiscriminator) { - w.line(`if (mode === 'raw') return item as ${baseType}`); - } else { - w.line("if (mode === 'raw') return item"); - } - if (sliceDef.constrainedChoice) { - const cc = sliceDef.constrainedChoice; - w.line( - `return unwrapSliceChoice<${flatTypeName}>(item, ${matchKeys}, ${JSON.stringify(cc.variant)})`, - ); - } else { - w.line(`return item as unknown as ${flatTypeName}`); - } - }, - ); + // Overload signatures + w.lineSM(`public ${getMethodName}(mode: 'flat'): ${flatTypeName}[]`); + w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType}[]`); + w.lineSM(`public ${getMethodName}(): ${defaultReturn}`); + + // Implementation + w.curlyBlock( + ["public", getMethodName, `(mode: 'flat' | 'raw' = '${defaultMode}'): (${flatTypeName} | ${baseType})[]`], + () => { + w.line(`const match = ${matchRef}`); + w.line(`const items = getArraySliceAll(${fieldAccess}, match)`); + if (sliceDef.typeDiscriminator) { + w.line(`if (mode === 'raw') return items as ${baseType}[]`); + } else { + w.line("if (mode === 'raw') return items"); + } + if (sliceDef.constrainedChoice) { + const cc = sliceDef.constrainedChoice; + w.line( + `return items.map(item => unwrapSliceChoice<${flatTypeName}>(item, ${matchKeys}, ${JSON.stringify(cc.variant)}))`, + ); + } else { + w.line(`return items as unknown as ${flatTypeName}[]`); + } + }, + ); + } else { + // Single-element slice: return single item or undefined + const defaultReturn = defaultMode === "raw" ? baseType : flatTypeName; + + // Overload signatures + w.lineSM(`public ${getMethodName}(mode: 'flat'): ${flatTypeName} | undefined`); + w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType} | undefined`); + w.lineSM(`public ${getMethodName}(): ${defaultReturn} | undefined`); + + // Implementation + w.curlyBlock( + [ + "public", + getMethodName, + `(mode: 'flat' | 'raw' = '${defaultMode}'): ${flatTypeName} | ${baseType} | undefined`, + ], + () => { + w.line(`const match = ${matchRef}`); + if (sliceDef.array) { + w.line(`const item = getArraySlice(${fieldAccess}, match)`); + w.line("if (!item) return undefined"); + } else { + w.line(`const item = ${fieldAccess}`); + w.line("if (!item || !matchesValue(item, match)) return undefined"); + } + if (sliceDef.typeDiscriminator) { + w.line(`if (mode === 'raw') return item as ${baseType}`); + } else { + w.line("if (mode === 'raw') return item"); + } + if (sliceDef.constrainedChoice) { + const cc = sliceDef.constrainedChoice; + w.line( + `return unwrapSliceChoice<${flatTypeName}>(item, ${matchKeys}, ${JSON.stringify(cc.variant)})`, + ); + } else { + w.line(`return item as unknown as ${flatTypeName}`); + } + }, + ); + } w.line(); } }; diff --git a/src/api/writer-generator/typescript/profile.ts b/src/api/writer-generator/typescript/profile.ts index 45b90da2..3d54a4d8 100644 --- a/src/api/writer-generator/typescript/profile.ts +++ b/src/api/writer-generator/typescript/profile.ts @@ -246,6 +246,8 @@ const generateProfileHelpersImport = ( if (canonicalUrl && hasMeta) imports.push("ensureProfile"); if (sliceDefs.length > 0 || factoryInfo.sliceAutoFields.length > 0) imports.push("applySliceMatch", "matchesValue", "setArraySlice", "getArraySlice", "ensureSliceDefaults"); + const hasUnboundedSlice = sliceDefs.some((s) => s.array && (s.max === 0 || s.max === undefined)); + if (hasUnboundedSlice) imports.push("setArraySliceAll", "getArraySliceAll"); if (extensions.some((ext) => ext.path.split(".").some((s) => s !== "extension"))) imports.push("ensurePath"); if (extensions.some((ext) => ext.isComplex && ext.subExtensions)) imports.push("extractComplexExtension"); if (sliceDefs.some((s) => s.constrainedChoice)) imports.push("wrapSliceChoice", "unwrapSliceChoice"); From 78d5bea04490ef4c0c56679fcfac06fd5364d637 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 6 Apr 2026 14:17:18 +0200 Subject: [PATCH 2/7] chore: Update snapshots for array slice API --- .../__snapshots__/typescript.test.ts.snap | 203 ++++++++---------- .../__snapshots__/local-package.test.ts.snap | 48 ++--- 2 files changed, 105 insertions(+), 146 deletions(-) diff --git a/test/api/write-generator/__snapshots__/typescript.test.ts.snap b/test/api/write-generator/__snapshots__/typescript.test.ts.snap index 27eed4b5..d29af3cb 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -323,17 +323,16 @@ import type { Quantity } from "../../hl7-fhir-r4-core/Quantity"; import type { Reference } from "../../hl7-fhir-r4-core/Reference"; export type Observation_bodyweight_Category_VSCatSliceFlat = Omit; -export type Observation_bodyweight_Category_VSCatSliceFlatAll = Observation_bodyweight_Category_VSCatSliceFlat & { - readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }]; -} import { + buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, + stripMatchKeys, validateRequired, validateExcluded, validateFixedValue, @@ -354,9 +353,7 @@ export type observation_bodyweightProfileRaw = { export class observation_bodyweightProfile { static readonly canonicalUrl = "http://hl7.org/fhir/StructureDefinition/bodyweight"; - private static readonly VSCatSliceMatch: Record = { - "coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}], - } + private static readonly VSCatSliceMatch: Record = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]}; private resource: Observation; @@ -392,14 +389,14 @@ export class observation_bodyweightProfile { observation_bodyweightProfile.VSCatSliceMatch, ); - const resource: Observation = { + const resource = buildResource( { resourceType: "Observation", code: {"coding":[{"code":"29463-7","system":"http://loinc.org"}]}, category: categoryWithDefaults, status: args.status, subject: args.subject, meta: { profile: [observation_bodyweightProfile.canonicalUrl] }, - } + }) return resource; } @@ -484,15 +481,15 @@ export class observation_bodyweightProfile { return this } - public getVSCat(mode: 'flat'): Observation_bodyweight_Category_VSCatSliceFlatAll | undefined; + public getVSCat(mode: 'flat'): Observation_bodyweight_Category_VSCatSliceFlat | undefined; public getVSCat(mode: 'raw'): CodeableConcept | undefined; - public getVSCat(): Observation_bodyweight_Category_VSCatSliceFlatAll | undefined; - public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bodyweight_Category_VSCatSliceFlatAll | CodeableConcept | undefined { + public getVSCat(): Observation_bodyweight_Category_VSCatSliceFlat | undefined; + public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bodyweight_Category_VSCatSliceFlat | CodeableConcept | undefined { const match = observation_bodyweightProfile.VSCatSliceMatch const item = getArraySlice(this.resource.category, match) if (!item) return undefined if (mode === 'raw') return item - return item as unknown as Observation_bodyweight_Category_VSCatSliceFlatAll + return stripMatchKeys(item, ["coding"]) } // Validation @@ -539,23 +536,18 @@ import type { Quantity } from "../../hl7-fhir-r4-core/Quantity"; import type { Reference } from "../../hl7-fhir-r4-core/Reference"; export type Observation_bp_Category_VSCatSliceFlat = Omit; -export type Observation_bp_Category_VSCatSliceFlatAll = Observation_bp_Category_VSCatSliceFlat & { - readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }]; -} - export type Observation_bp_Component_SystolicBPSliceFlat = Omit & Quantity; -export type Observation_bp_Component_SystolicBPSliceFlatAll = Observation_bp_Component_SystolicBPSliceFlat; - export type Observation_bp_Component_DiastolicBPSliceFlat = Omit & Quantity; -export type Observation_bp_Component_DiastolicBPSliceFlatAll = Observation_bp_Component_DiastolicBPSliceFlat; import { + buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, + stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, validateRequired, @@ -579,15 +571,9 @@ export type observation_bpProfileRaw = { export class observation_bpProfile { static readonly canonicalUrl = "http://hl7.org/fhir/StructureDefinition/bp"; - private static readonly VSCatSliceMatch: Record = { - "coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}], - } - private static readonly SystolicBPSliceMatch: Record = { - "code": {"coding":[{"code":"8480-6","system":"http://loinc.org"}]}, - } - private static readonly DiastolicBPSliceMatch: Record = { - "code": {"coding":[{"code":"8462-4","system":"http://loinc.org"}]}, - } + private static readonly VSCatSliceMatch: Record = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]}; + private static readonly SystolicBPSliceMatch: Record = {"code":{"coding":[{"code":"8480-6","system":"http://loinc.org"}]}}; + private static readonly DiastolicBPSliceMatch: Record = {"code":{"coding":[{"code":"8462-4","system":"http://loinc.org"}]}}; private resource: Observation; @@ -633,7 +619,7 @@ export class observation_bpProfile { observation_bpProfile.DiastolicBPSliceMatch, ); - const resource: Observation = { + const resource = buildResource( { resourceType: "Observation", code: {"coding":[{"code":"85354-9","system":"http://loinc.org"}]}, category: categoryWithDefaults, @@ -641,7 +627,7 @@ export class observation_bpProfile { status: args.status, subject: args.subject, meta: { profile: [observation_bpProfile.canonicalUrl] }, - } + }) return resource; } @@ -759,37 +745,37 @@ export class observation_bpProfile { return this } - public getVSCat(mode: 'flat'): Observation_bp_Category_VSCatSliceFlatAll | undefined; + public getVSCat(mode: 'flat'): Observation_bp_Category_VSCatSliceFlat | undefined; public getVSCat(mode: 'raw'): CodeableConcept | undefined; - public getVSCat(): Observation_bp_Category_VSCatSliceFlatAll | undefined; - public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Category_VSCatSliceFlatAll | CodeableConcept | undefined { + public getVSCat(): Observation_bp_Category_VSCatSliceFlat | undefined; + public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Category_VSCatSliceFlat | CodeableConcept | undefined { const match = observation_bpProfile.VSCatSliceMatch const item = getArraySlice(this.resource.category, match) if (!item) return undefined if (mode === 'raw') return item - return item as unknown as Observation_bp_Category_VSCatSliceFlatAll + return stripMatchKeys(item, ["coding"]) } - public getSystolicBP(mode: 'flat'): Observation_bp_Component_SystolicBPSliceFlatAll | undefined; + public getSystolicBP(mode: 'flat'): Observation_bp_Component_SystolicBPSliceFlat | undefined; public getSystolicBP(mode: 'raw'): ObservationComponent | undefined; - public getSystolicBP(): Observation_bp_Component_SystolicBPSliceFlatAll | undefined; - public getSystolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_SystolicBPSliceFlatAll | ObservationComponent | undefined { + public getSystolicBP(): Observation_bp_Component_SystolicBPSliceFlat | undefined; + public getSystolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_SystolicBPSliceFlat | ObservationComponent | undefined { const match = observation_bpProfile.SystolicBPSliceMatch const item = getArraySlice(this.resource.component, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["code"], "valueQuantity") + return unwrapSliceChoice(item, ["code"], "valueQuantity") } - public getDiastolicBP(mode: 'flat'): Observation_bp_Component_DiastolicBPSliceFlatAll | undefined; + public getDiastolicBP(mode: 'flat'): Observation_bp_Component_DiastolicBPSliceFlat | undefined; public getDiastolicBP(mode: 'raw'): ObservationComponent | undefined; - public getDiastolicBP(): Observation_bp_Component_DiastolicBPSliceFlatAll | undefined; - public getDiastolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_DiastolicBPSliceFlatAll | ObservationComponent | undefined { + public getDiastolicBP(): Observation_bp_Component_DiastolicBPSliceFlat | undefined; + public getDiastolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_DiastolicBPSliceFlat | ObservationComponent | undefined { const match = observation_bpProfile.DiastolicBPSliceMatch const item = getArraySlice(this.resource.component, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["code"], "valueQuantity") + return unwrapSliceChoice(item, ["code"], "valueQuantity") } // Validation @@ -851,6 +837,7 @@ import { } from "./Extension_USCoreTribalAffiliationExtension"; import { + buildResource, ensureProfile, extractComplexExtension, isExtension, @@ -898,12 +885,12 @@ export class USCorePatientProfile { } static createResource (args: USCorePatientProfileRaw) : Patient { - const resource: Patient = { + const resource = buildResource( { resourceType: "Patient", identifier: args.identifier, name: args.name, meta: { profile: [USCorePatientProfile.canonicalUrl] }, - } + }) return resource; } @@ -1096,23 +1083,18 @@ import type { Reference } from "../../hl7-fhir-r4-core/Reference"; import type { SampledData } from "../../hl7-fhir-r4-core/SampledData"; export type USCoreBloodPressureProfile_Category_VSCatSliceFlat = Omit; -export type USCoreBloodPressureProfile_Category_VSCatSliceFlatAll = USCoreBloodPressureProfile_Category_VSCatSliceFlat & { - readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }]; -} - export type USCoreBloodPressureProfile_Component_SystolicSliceFlat = Omit & Quantity; -export type USCoreBloodPressureProfile_Component_SystolicSliceFlatAll = USCoreBloodPressureProfile_Component_SystolicSliceFlat; - export type USCoreBloodPressureProfile_Component_DiastolicSliceFlat = Omit & Quantity; -export type USCoreBloodPressureProfile_Component_DiastolicSliceFlatAll = USCoreBloodPressureProfile_Component_DiastolicSliceFlat; import { + buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, + stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, validateRequired, @@ -1136,15 +1118,9 @@ export type USCoreBloodPressureProfileRaw = { export class USCoreBloodPressureProfile { static readonly canonicalUrl = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure"; - private static readonly VSCatSliceMatch: Record = { - "coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}], - } - private static readonly systolicSliceMatch: Record = { - "code": {"coding":[{"system":"http://loinc.org","code":"8480-6"}]}, - } - private static readonly diastolicSliceMatch: Record = { - "code": {"coding":[{"system":"http://loinc.org","code":"8462-4"}]}, - } + private static readonly VSCatSliceMatch: Record = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]}; + private static readonly systolicSliceMatch: Record = {"code":{"coding":[{"system":"http://loinc.org","code":"8480-6"}]}}; + private static readonly diastolicSliceMatch: Record = {"code":{"coding":[{"system":"http://loinc.org","code":"8462-4"}]}}; private resource: Observation; @@ -1190,7 +1166,7 @@ export class USCoreBloodPressureProfile { USCoreBloodPressureProfile.diastolicSliceMatch, ); - const resource: Observation = { + const resource = buildResource( { resourceType: "Observation", code: {"coding":[{"system":"http://loinc.org","code":"85354-9"}]}, category: categoryWithDefaults, @@ -1198,7 +1174,7 @@ export class USCoreBloodPressureProfile { status: args.status, subject: args.subject, meta: { profile: [USCoreBloodPressureProfile.canonicalUrl] }, - } + }) return resource; } @@ -1406,37 +1382,37 @@ export class USCoreBloodPressureProfile { return this } - public getVSCat(mode: 'flat'): USCoreBloodPressureProfile_Category_VSCatSliceFlatAll | undefined; + public getVSCat(mode: 'flat'): USCoreBloodPressureProfile_Category_VSCatSliceFlat | undefined; public getVSCat(mode: 'raw'): CodeableConcept | undefined; - public getVSCat(): USCoreBloodPressureProfile_Category_VSCatSliceFlatAll | undefined; - public getVSCat (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Category_VSCatSliceFlatAll | CodeableConcept | undefined { + public getVSCat(): USCoreBloodPressureProfile_Category_VSCatSliceFlat | undefined; + public getVSCat (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Category_VSCatSliceFlat | CodeableConcept | undefined { const match = USCoreBloodPressureProfile.VSCatSliceMatch const item = getArraySlice(this.resource.category, match) if (!item) return undefined if (mode === 'raw') return item - return item as unknown as USCoreBloodPressureProfile_Category_VSCatSliceFlatAll + return stripMatchKeys(item, ["coding"]) } - public getSystolic(mode: 'flat'): USCoreBloodPressureProfile_Component_SystolicSliceFlatAll | undefined; + public getSystolic(mode: 'flat'): USCoreBloodPressureProfile_Component_SystolicSliceFlat | undefined; public getSystolic(mode: 'raw'): ObservationComponent | undefined; - public getSystolic(): USCoreBloodPressureProfile_Component_SystolicSliceFlatAll | undefined; - public getSystolic (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Component_SystolicSliceFlatAll | ObservationComponent | undefined { + public getSystolic(): USCoreBloodPressureProfile_Component_SystolicSliceFlat | undefined; + public getSystolic (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Component_SystolicSliceFlat | ObservationComponent | undefined { const match = USCoreBloodPressureProfile.systolicSliceMatch const item = getArraySlice(this.resource.component, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["code"], "valueQuantity") + return unwrapSliceChoice(item, ["code"], "valueQuantity") } - public getDiastolic(mode: 'flat'): USCoreBloodPressureProfile_Component_DiastolicSliceFlatAll | undefined; + public getDiastolic(mode: 'flat'): USCoreBloodPressureProfile_Component_DiastolicSliceFlat | undefined; public getDiastolic(mode: 'raw'): ObservationComponent | undefined; - public getDiastolic(): USCoreBloodPressureProfile_Component_DiastolicSliceFlatAll | undefined; - public getDiastolic (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Component_DiastolicSliceFlatAll | ObservationComponent | undefined { + public getDiastolic(): USCoreBloodPressureProfile_Component_DiastolicSliceFlat | undefined; + public getDiastolic (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Component_DiastolicSliceFlat | ObservationComponent | undefined { const match = USCoreBloodPressureProfile.diastolicSliceMatch const item = getArraySlice(this.resource.component, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["code"], "valueQuantity") + return unwrapSliceChoice(item, ["code"], "valueQuantity") } // Validation @@ -1490,17 +1466,16 @@ import type { Reference } from "../../hl7-fhir-r4-core/Reference"; import type { SampledData } from "../../hl7-fhir-r4-core/SampledData"; export type USCoreBodyWeightProfile_Category_VSCatSliceFlat = Omit; -export type USCoreBodyWeightProfile_Category_VSCatSliceFlatAll = USCoreBodyWeightProfile_Category_VSCatSliceFlat & { - readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }]; -} import { + buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, + stripMatchKeys, validateRequired, validateExcluded, validateFixedValue, @@ -1521,9 +1496,7 @@ export type USCoreBodyWeightProfileRaw = { export class USCoreBodyWeightProfile { static readonly canonicalUrl = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-weight"; - private static readonly VSCatSliceMatch: Record = { - "coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}], - } + private static readonly VSCatSliceMatch: Record = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]}; private resource: Observation; @@ -1559,14 +1532,14 @@ export class USCoreBodyWeightProfile { USCoreBodyWeightProfile.VSCatSliceMatch, ); - const resource: Observation = { + const resource = buildResource( { resourceType: "Observation", code: {"coding":[{"system":"http://loinc.org","code":"29463-7"}]}, category: categoryWithDefaults, status: args.status, subject: args.subject, meta: { profile: [USCoreBodyWeightProfile.canonicalUrl] }, - } + }) return resource; } @@ -1741,15 +1714,15 @@ export class USCoreBodyWeightProfile { return this } - public getVSCat(mode: 'flat'): USCoreBodyWeightProfile_Category_VSCatSliceFlatAll | undefined; + public getVSCat(mode: 'flat'): USCoreBodyWeightProfile_Category_VSCatSliceFlat | undefined; public getVSCat(mode: 'raw'): CodeableConcept | undefined; - public getVSCat(): USCoreBodyWeightProfile_Category_VSCatSliceFlatAll | undefined; - public getVSCat (mode: 'flat' | 'raw' = 'flat'): USCoreBodyWeightProfile_Category_VSCatSliceFlatAll | CodeableConcept | undefined { + public getVSCat(): USCoreBodyWeightProfile_Category_VSCatSliceFlat | undefined; + public getVSCat (mode: 'flat' | 'raw' = 'flat'): USCoreBodyWeightProfile_Category_VSCatSliceFlat | CodeableConcept | undefined { const match = USCoreBodyWeightProfile.VSCatSliceMatch const item = getArraySlice(this.resource.category, match) if (!item) return undefined if (mode === 'raw') return item - return item as unknown as USCoreBodyWeightProfile_Category_VSCatSliceFlatAll + return stripMatchKeys(item, ["coding"]) } // Validation @@ -1805,23 +1778,20 @@ import type { Coding } from "../../hl7-fhir-r4-core/Coding"; import type { Extension } from "../../hl7-fhir-r4-core/Extension"; export type USCoreRaceExtension_Extension_OmbCategorySliceFlat = Omit & Coding; -export type USCoreRaceExtension_Extension_OmbCategorySliceFlatAll = USCoreRaceExtension_Extension_OmbCategorySliceFlat; - export type USCoreRaceExtension_Extension_DetailedSliceFlat = Omit & Coding; -export type USCoreRaceExtension_Extension_DetailedSliceFlatAll = USCoreRaceExtension_Extension_DetailedSliceFlat; - export type USCoreRaceExtension_Extension_TextSliceFlat = Omit; -export type USCoreRaceExtension_Extension_TextSliceFlatAll = USCoreRaceExtension_Extension_TextSliceFlat & { - readonly url: "text"; -} import { + buildResource, isRawExtensionInput, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, + setArraySliceAll, + getArraySliceAll, + stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, isExtension, @@ -1900,10 +1870,10 @@ export class USCoreRaceExtensionProfile { static createResource (args: USCoreRaceExtensionProfileRaw | USCoreRaceExtensionProfileFlat) : Extension { const resolvedExtensions = USCoreRaceExtensionProfile.resolveInput(args ?? {}); - const resource: Extension = { + const resource = buildResource( { url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", extension: resolvedExtensions, - } + }) return resource; } @@ -1985,15 +1955,11 @@ export class USCoreRaceExtensionProfile { return this } - public setExtensionDetailed (input?: USCoreRaceExtension_Extension_DetailedSliceFlat | Extension): this { + public setExtensionDetailed (input: (USCoreRaceExtension_Extension_DetailedSliceFlat | Extension)[]): this { const match = USCoreRaceExtensionProfile.detailedSliceMatch - if (input && matchesValue(input, match)) { - setArraySlice(this.resource.extension ??= [], match, input as Extension) - return this - } - const wrapped = wrapSliceChoice(input ?? {}, "valueCoding") - const value = applySliceMatch(wrapped, match) - setArraySlice(this.resource.extension ??= [], match, value) + const arr = this.resource.extension ??= [] + const values = input.map(item => matchesValue(item, match) ? item as Extension : applySliceMatch(wrapSliceChoice(item, "valueCoding"), match)) + setArraySliceAll(arr, match, values) return this } @@ -2008,37 +1974,36 @@ export class USCoreRaceExtensionProfile { return this } - public getExtensionOmbCategory(mode: 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | undefined; + public getExtensionOmbCategory(mode: 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlat | undefined; public getExtensionOmbCategory(mode: 'raw'): Extension | undefined; - public getExtensionOmbCategory(): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | undefined; - public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | Extension | undefined { + public getExtensionOmbCategory(): USCoreRaceExtension_Extension_OmbCategorySliceFlat | undefined; + public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlat | Extension | undefined { const match = USCoreRaceExtensionProfile.ombCategorySliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll | undefined; - public getExtensionDetailed(mode: 'raw'): Extension | undefined; - public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlatAll | undefined; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll | Extension | undefined { + public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed(mode: 'raw'): Extension[]; + public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlat | Extension)[] { const match = USCoreRaceExtensionProfile.detailedSliceMatch - const item = getArraySlice(this.resource.extension, match) - if (!item) return undefined - if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + const items = getArraySliceAll(this.resource.extension, match) + if (mode === 'raw') return items + return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } - public getExtensionText(mode: 'flat'): USCoreRaceExtension_Extension_TextSliceFlatAll | undefined; + public getExtensionText(mode: 'flat'): USCoreRaceExtension_Extension_TextSliceFlat | undefined; public getExtensionText(mode: 'raw'): Extension | undefined; - public getExtensionText(): USCoreRaceExtension_Extension_TextSliceFlatAll | undefined; - public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_TextSliceFlatAll | Extension | undefined { + public getExtensionText(): USCoreRaceExtension_Extension_TextSliceFlat | undefined; + public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_TextSliceFlat | Extension | undefined { const match = USCoreRaceExtensionProfile.textSliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return item as unknown as USCoreRaceExtension_Extension_TextSliceFlatAll + return stripMatchKeys(item, ["url"]) } // Validation diff --git a/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap index 0057d278..a360cc58 100644 --- a/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap +++ b/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap @@ -38,18 +38,18 @@ import type { Organization } from "../../hl7-fhir-r4-core/Organization"; import type { Patient } from "../../hl7-fhir-r4-core/Patient"; export type ExampleTypedBundle_Entry_PatientEntrySliceFlat = BundleEntry; -export type ExampleTypedBundle_Entry_PatientEntrySliceFlatAll = ExampleTypedBundle_Entry_PatientEntrySliceFlat; - export type ExampleTypedBundle_Entry_OrganizationEntrySliceFlat = BundleEntry; -export type ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll = ExampleTypedBundle_Entry_OrganizationEntrySliceFlat; import { + buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, + setArraySliceAll, + getArraySliceAll, validateRequired, validateExcluded, validateFixedValue, @@ -69,9 +69,7 @@ export class ExampleTypedBundleProfile { static readonly canonicalUrl = "http://example.org/fhir/StructureDefinition/ExampleTypedBundle"; private static readonly PatientEntrySliceMatch: Record = {"resource":{"resourceType":"Patient"}}; - private static readonly OrganizationEntrySliceMatch: Record = { - "resource": {"resourceType":"Organization"}, - } + private static readonly OrganizationEntrySliceMatch: Record = {"resource":{"resourceType":"Organization"}}; private resource: Bundle; @@ -95,11 +93,11 @@ export class ExampleTypedBundleProfile { } static createResource (args: ExampleTypedBundleProfileRaw) : Bundle { - const resource: Bundle = { + const resource = buildResource( { resourceType: "Bundle", type: args.type, meta: { profile: [ExampleTypedBundleProfile.canonicalUrl] }, - } + }) return resource; } @@ -135,37 +133,33 @@ export class ExampleTypedBundleProfile { return this } - public setOrganizationEntry (input?: ExampleTypedBundle_Entry_OrganizationEntrySliceFlat | BundleEntry): this { + public setOrganizationEntry (input: (ExampleTypedBundle_Entry_OrganizationEntrySliceFlat | BundleEntry)[]): this { const match = ExampleTypedBundleProfile.OrganizationEntrySliceMatch - if (input && matchesValue(input, match)) { - setArraySlice(this.resource.entry ??= [], match, input as BundleEntry) - return this - } - const value = applySliceMatch>(input ?? {}, match) - setArraySlice(this.resource.entry ??= [], match, value) + const arr = this.resource.entry ??= [] + const values = input.map(item => matchesValue(item, match) ? item as BundleEntry : applySliceMatch>(item, match)) + setArraySliceAll(arr, match, values) return this } - public getPatientEntry(mode: 'flat'): ExampleTypedBundle_Entry_PatientEntrySliceFlatAll | undefined; + public getPatientEntry(mode: 'flat'): ExampleTypedBundle_Entry_PatientEntrySliceFlat | undefined; public getPatientEntry(mode: 'raw'): BundleEntry | undefined; - public getPatientEntry(): ExampleTypedBundle_Entry_PatientEntrySliceFlatAll | undefined; - public getPatientEntry (mode: 'flat' | 'raw' = 'flat'): ExampleTypedBundle_Entry_PatientEntrySliceFlatAll | BundleEntry | undefined { + public getPatientEntry(): ExampleTypedBundle_Entry_PatientEntrySliceFlat | undefined; + public getPatientEntry (mode: 'flat' | 'raw' = 'flat'): ExampleTypedBundle_Entry_PatientEntrySliceFlat | BundleEntry | undefined { const match = ExampleTypedBundleProfile.PatientEntrySliceMatch const item = getArraySlice(this.resource.entry, match) if (!item) return undefined if (mode === 'raw') return item as BundleEntry - return item as unknown as ExampleTypedBundle_Entry_PatientEntrySliceFlatAll + return item as ExampleTypedBundle_Entry_PatientEntrySliceFlat } - public getOrganizationEntry(mode: 'flat'): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll | undefined; - public getOrganizationEntry(mode: 'raw'): BundleEntry | undefined; - public getOrganizationEntry(): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll | undefined; - public getOrganizationEntry (mode: 'flat' | 'raw' = 'flat'): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll | BundleEntry | undefined { + public getOrganizationEntry(mode: 'flat'): ExampleTypedBundle_Entry_OrganizationEntrySliceFlat[]; + public getOrganizationEntry(mode: 'raw'): BundleEntry[]; + public getOrganizationEntry(): ExampleTypedBundle_Entry_OrganizationEntrySliceFlat[]; + public getOrganizationEntry (mode: 'flat' | 'raw' = 'flat'): (ExampleTypedBundle_Entry_OrganizationEntrySliceFlat | BundleEntry)[] { const match = ExampleTypedBundleProfile.OrganizationEntrySliceMatch - const item = getArraySlice(this.resource.entry, match) - if (!item) return undefined - if (mode === 'raw') return item as BundleEntry - return item as unknown as ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll + const items = getArraySliceAll(this.resource.entry, match) + if (mode === 'raw') return items as BundleEntry[] + return items as ExampleTypedBundle_Entry_OrganizationEntrySliceFlat[] } // Validation From 69058831040d9eaee2fe5e8b95c5d3408f4423af Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 6 Apr 2026 14:17:21 +0200 Subject: [PATCH 3/7] chore: Regenerate examples and update tests for array slice API --- .../profile-typed-bundle.test.ts | 28 +++++++-- .../fhir-types/profile-helpers.ts | 20 +++++++ .../Extension_USCoreEthnicityExtension.ts | 58 ++++++++----------- .../profiles/Extension_USCoreRaceExtension.ts | 58 ++++++++----------- .../fhir-types/profile-helpers.ts | 20 +++++++ 5 files changed, 112 insertions(+), 72 deletions(-) diff --git a/examples/local-package-folder/profile-typed-bundle.test.ts b/examples/local-package-folder/profile-typed-bundle.test.ts index a47a5feb..5753633b 100644 --- a/examples/local-package-folder/profile-typed-bundle.test.ts +++ b/examples/local-package-folder/profile-typed-bundle.test.ts @@ -58,21 +58,37 @@ describe("type-discriminated bundle slices", () => { test("fluent chaining across slice setters", () => { const bundle = ExampleTypedBundleProfile.create({ type: "collection" }) .setPatientEntry({ resource: activePatient }) - .setOrganizationEntry({ resource: clinicOrg }); + .setOrganizationEntry([{ resource: clinicOrg }]); expect(bundle.toResource().entry).toHaveLength(2); expect(bundle.getPatientEntry()!.resource).toEqual(activePatient); - expect(bundle.getOrganizationEntry()!.resource).toEqual(clinicOrg); + expect(bundle.getOrganizationEntry()[0]!.resource).toEqual(clinicOrg); }); - test("setOrganizationEntry replaces existing org entry (same discriminator)", () => { + test("setOrganizationEntry replaces all org entries (unbounded slice)", () => { const bundle = ExampleTypedBundleProfile.create({ type: "collection" }) .setPatientEntry({ resource: activePatient }) - .setOrganizationEntry({ resource: clinicOrg }) - .setOrganizationEntry({ resource: { resourceType: "Organization", name: "Acme" } }); + .setOrganizationEntry([{ resource: clinicOrg }]) + .setOrganizationEntry([{ resource: { resourceType: "Organization", name: "Acme" } }]); const entries = bundle.toResource().entry!; expect(entries).toHaveLength(2); - expect(bundle.getOrganizationEntry()!.resource!.name).toBe("Acme"); + expect(bundle.getOrganizationEntry()[0]!.resource!.name).toBe("Acme"); + }); + + test("setOrganizationEntry supports multiple items (max: *)", () => { + const bundle = ExampleTypedBundleProfile.create({ type: "collection" }) + .setPatientEntry({ resource: activePatient }) + .setOrganizationEntry([ + { resource: clinicOrg }, + { resource: { resourceType: "Organization", name: "Acme" } }, + ]); + + const entries = bundle.toResource().entry!; + expect(entries).toHaveLength(3); + const orgs = bundle.getOrganizationEntry(); + expect(orgs).toHaveLength(2); + expect(orgs[0]!.resource).toEqual(clinicOrg); + expect(orgs[1]!.resource!.name).toBe("Acme"); }); }); diff --git a/examples/typescript-r4/fhir-types/profile-helpers.ts b/examples/typescript-r4/fhir-types/profile-helpers.ts index 239ec0d0..2c159297 100644 --- a/examples/typescript-r4/fhir-types/profile-helpers.ts +++ b/examples/typescript-r4/fhir-types/profile-helpers.ts @@ -296,6 +296,26 @@ export const getArraySlice = (list: readonly T[] | undefined, match: Record matchesValue(item, match)); }; +/** Return all elements in `list` that satisfy the slice discriminator `match`. */ +export const getArraySliceAll = (list: readonly T[] | undefined, match: Record): T[] => { + if (!list) return []; + return list.filter((item) => matchesValue(item, match)); +}; + +/** + * Replace all elements matching `match` in `list` with `newItems`. + * Each new item has the discriminator values applied via {@link applySliceMatch} + * before this call, so this helper only handles the array surgery. + */ +export const setArraySliceAll = (list: T[], match: Record, newItems: T[]): void => { + // Remove all existing items that match the discriminator + for (let i = list.length - 1; i >= 0; i--) { + if (matchesValue(list[i], match)) list.splice(i, 1); + } + // Append new items + list.push(...newItems); +}; + // --------------------------------------------------------------------------- // Validation helpers // 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 4794ccf3..5503ac9e 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 @@ -6,23 +6,20 @@ import type { Coding } from "../../hl7-fhir-r4-core/Coding"; import type { Extension } from "../../hl7-fhir-r4-core/Extension"; export type USCoreEthnicityExtension_Extension_OmbCategorySliceFlat = Omit & Coding; -export type USCoreEthnicityExtension_Extension_OmbCategorySliceFlatAll = USCoreEthnicityExtension_Extension_OmbCategorySliceFlat; - export type USCoreEthnicityExtension_Extension_DetailedSliceFlat = Omit & Coding; -export type USCoreEthnicityExtension_Extension_DetailedSliceFlatAll = USCoreEthnicityExtension_Extension_DetailedSliceFlat; - export type USCoreEthnicityExtension_Extension_TextSliceFlat = Omit; -export type USCoreEthnicityExtension_Extension_TextSliceFlatAll = USCoreEthnicityExtension_Extension_TextSliceFlat & { - readonly url: "text"; -} import { + buildResource, isRawExtensionInput, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, + setArraySliceAll, + getArraySliceAll, + stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, isExtension, @@ -101,10 +98,10 @@ export class USCoreEthnicityExtensionProfile { static createResource (args: USCoreEthnicityExtensionProfileRaw | USCoreEthnicityExtensionProfileFlat) : Extension { const resolvedExtensions = USCoreEthnicityExtensionProfile.resolveInput(args ?? {}); - const resource: Extension = { + const resource = buildResource( { url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", extension: resolvedExtensions, - } + }) return resource; } @@ -186,15 +183,11 @@ export class USCoreEthnicityExtensionProfile { return this } - public setExtensionDetailed (input?: USCoreEthnicityExtension_Extension_DetailedSliceFlat | Extension): this { + public setExtensionDetailed (input: (USCoreEthnicityExtension_Extension_DetailedSliceFlat | Extension)[]): this { const match = USCoreEthnicityExtensionProfile.detailedSliceMatch - if (input && matchesValue(input, match)) { - setArraySlice(this.resource.extension ??= [], match, input as Extension) - return this - } - const wrapped = wrapSliceChoice(input ?? {}, "valueCoding") - const value = applySliceMatch(wrapped, match) - setArraySlice(this.resource.extension ??= [], match, value) + const arr = this.resource.extension ??= [] + const values = input.map(item => matchesValue(item, match) ? item as Extension : applySliceMatch(wrapSliceChoice(item, "valueCoding"), match)) + setArraySliceAll(arr, match, values) return this } @@ -209,37 +202,36 @@ export class USCoreEthnicityExtensionProfile { return this } - public getExtensionOmbCategory(mode: 'flat'): USCoreEthnicityExtension_Extension_OmbCategorySliceFlatAll | undefined; + public getExtensionOmbCategory(mode: 'flat'): USCoreEthnicityExtension_Extension_OmbCategorySliceFlat | undefined; public getExtensionOmbCategory(mode: 'raw'): Extension | undefined; - public getExtensionOmbCategory(): USCoreEthnicityExtension_Extension_OmbCategorySliceFlatAll | undefined; - public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_OmbCategorySliceFlatAll | Extension | undefined { + public getExtensionOmbCategory(): USCoreEthnicityExtension_Extension_OmbCategorySliceFlat | undefined; + public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_OmbCategorySliceFlat | Extension | undefined { const match = USCoreEthnicityExtensionProfile.ombCategorySliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll | undefined; - public getExtensionDetailed(mode: 'raw'): Extension | undefined; - public getExtensionDetailed(): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll | undefined; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll | Extension | undefined { + public getExtensionDetailed(mode: 'flat'): USCoreEthnicityExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed(mode: 'raw'): Extension[]; + public getExtensionDetailed(): USCoreEthnicityExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreEthnicityExtension_Extension_DetailedSliceFlat | Extension)[] { const match = USCoreEthnicityExtensionProfile.detailedSliceMatch - const item = getArraySlice(this.resource.extension, match) - if (!item) return undefined - if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + const items = getArraySliceAll(this.resource.extension, match) + if (mode === 'raw') return items + return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } - public getExtensionText(mode: 'flat'): USCoreEthnicityExtension_Extension_TextSliceFlatAll | undefined; + public getExtensionText(mode: 'flat'): USCoreEthnicityExtension_Extension_TextSliceFlat | undefined; public getExtensionText(mode: 'raw'): Extension | undefined; - public getExtensionText(): USCoreEthnicityExtension_Extension_TextSliceFlatAll | undefined; - public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_TextSliceFlatAll | Extension | undefined { + public getExtensionText(): USCoreEthnicityExtension_Extension_TextSliceFlat | undefined; + public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_TextSliceFlat | Extension | undefined { const match = USCoreEthnicityExtensionProfile.textSliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return item as unknown as USCoreEthnicityExtension_Extension_TextSliceFlatAll + return stripMatchKeys(item, ["url"]) } // Validation 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 da6e4171..d5afa68d 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 @@ -6,23 +6,20 @@ import type { Coding } from "../../hl7-fhir-r4-core/Coding"; import type { Extension } from "../../hl7-fhir-r4-core/Extension"; export type USCoreRaceExtension_Extension_OmbCategorySliceFlat = Omit & Coding; -export type USCoreRaceExtension_Extension_OmbCategorySliceFlatAll = USCoreRaceExtension_Extension_OmbCategorySliceFlat; - export type USCoreRaceExtension_Extension_DetailedSliceFlat = Omit & Coding; -export type USCoreRaceExtension_Extension_DetailedSliceFlatAll = USCoreRaceExtension_Extension_DetailedSliceFlat; - export type USCoreRaceExtension_Extension_TextSliceFlat = Omit; -export type USCoreRaceExtension_Extension_TextSliceFlatAll = USCoreRaceExtension_Extension_TextSliceFlat & { - readonly url: "text"; -} import { + buildResource, isRawExtensionInput, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, + setArraySliceAll, + getArraySliceAll, + stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, isExtension, @@ -101,10 +98,10 @@ export class USCoreRaceExtensionProfile { static createResource (args: USCoreRaceExtensionProfileRaw | USCoreRaceExtensionProfileFlat) : Extension { const resolvedExtensions = USCoreRaceExtensionProfile.resolveInput(args ?? {}); - const resource: Extension = { + const resource = buildResource( { url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", extension: resolvedExtensions, - } + }) return resource; } @@ -186,15 +183,11 @@ export class USCoreRaceExtensionProfile { return this } - public setExtensionDetailed (input?: USCoreRaceExtension_Extension_DetailedSliceFlat | Extension): this { + public setExtensionDetailed (input: (USCoreRaceExtension_Extension_DetailedSliceFlat | Extension)[]): this { const match = USCoreRaceExtensionProfile.detailedSliceMatch - if (input && matchesValue(input, match)) { - setArraySlice(this.resource.extension ??= [], match, input as Extension) - return this - } - const wrapped = wrapSliceChoice(input ?? {}, "valueCoding") - const value = applySliceMatch(wrapped, match) - setArraySlice(this.resource.extension ??= [], match, value) + const arr = this.resource.extension ??= [] + const values = input.map(item => matchesValue(item, match) ? item as Extension : applySliceMatch(wrapSliceChoice(item, "valueCoding"), match)) + setArraySliceAll(arr, match, values) return this } @@ -209,37 +202,36 @@ export class USCoreRaceExtensionProfile { return this } - public getExtensionOmbCategory(mode: 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | undefined; + public getExtensionOmbCategory(mode: 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlat | undefined; public getExtensionOmbCategory(mode: 'raw'): Extension | undefined; - public getExtensionOmbCategory(): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | undefined; - public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | Extension | undefined { + public getExtensionOmbCategory(): USCoreRaceExtension_Extension_OmbCategorySliceFlat | undefined; + public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlat | Extension | undefined { const match = USCoreRaceExtensionProfile.ombCategorySliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll | undefined; - public getExtensionDetailed(mode: 'raw'): Extension | undefined; - public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlatAll | undefined; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll | Extension | undefined { + public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed(mode: 'raw'): Extension[]; + public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlat | Extension)[] { const match = USCoreRaceExtensionProfile.detailedSliceMatch - const item = getArraySlice(this.resource.extension, match) - if (!item) return undefined - if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + const items = getArraySliceAll(this.resource.extension, match) + if (mode === 'raw') return items + return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } - public getExtensionText(mode: 'flat'): USCoreRaceExtension_Extension_TextSliceFlatAll | undefined; + public getExtensionText(mode: 'flat'): USCoreRaceExtension_Extension_TextSliceFlat | undefined; public getExtensionText(mode: 'raw'): Extension | undefined; - public getExtensionText(): USCoreRaceExtension_Extension_TextSliceFlatAll | undefined; - public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_TextSliceFlatAll | Extension | undefined { + public getExtensionText(): USCoreRaceExtension_Extension_TextSliceFlat | undefined; + public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_TextSliceFlat | Extension | undefined { const match = USCoreRaceExtensionProfile.textSliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return item as unknown as USCoreRaceExtension_Extension_TextSliceFlatAll + return stripMatchKeys(item, ["url"]) } // Validation diff --git a/examples/typescript-us-core/fhir-types/profile-helpers.ts b/examples/typescript-us-core/fhir-types/profile-helpers.ts index 239ec0d0..2c159297 100644 --- a/examples/typescript-us-core/fhir-types/profile-helpers.ts +++ b/examples/typescript-us-core/fhir-types/profile-helpers.ts @@ -296,6 +296,26 @@ export const getArraySlice = (list: readonly T[] | undefined, match: Record matchesValue(item, match)); }; +/** Return all elements in `list` that satisfy the slice discriminator `match`. */ +export const getArraySliceAll = (list: readonly T[] | undefined, match: Record): T[] => { + if (!list) return []; + return list.filter((item) => matchesValue(item, match)); +}; + +/** + * Replace all elements matching `match` in `list` with `newItems`. + * Each new item has the discriminator values applied via {@link applySliceMatch} + * before this call, so this helper only handles the array surgery. + */ +export const setArraySliceAll = (list: T[], match: Record, newItems: T[]): void => { + // Remove all existing items that match the discriminator + for (let i = list.length - 1; i >= 0; i--) { + if (matchesValue(list[i], match)) list.splice(i, 1); + } + // Append new items + list.push(...newItems); +}; + // --------------------------------------------------------------------------- // Validation helpers // From e386c4fdb7dcb0d684d92ccdea407c7377a494a5 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 6 Apr 2026 22:44:58 +0200 Subject: [PATCH 4/7] fix: Resolve rebase conflicts, regenerate snapshots and examples --- .../Extension_USCoreEthnicityExtension.ts | 37 ++-- .../profiles/Extension_USCoreRaceExtension.ts | 37 ++-- .../typescript/profile-slices.ts | 6 +- .../__snapshots__/typescript.test.ts.snap | 182 ++++++++++-------- .../__snapshots__/local-package.test.ts.snap | 28 +-- 5 files changed, 170 insertions(+), 120 deletions(-) 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 5503ac9e..eb0dca69 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 @@ -6,11 +6,17 @@ import type { Coding } from "../../hl7-fhir-r4-core/Coding"; import type { Extension } from "../../hl7-fhir-r4-core/Extension"; export type USCoreEthnicityExtension_Extension_OmbCategorySliceFlat = Omit & Coding; +export type USCoreEthnicityExtension_Extension_OmbCategorySliceFlatAll = USCoreEthnicityExtension_Extension_OmbCategorySliceFlat; + export type USCoreEthnicityExtension_Extension_DetailedSliceFlat = Omit & Coding; +export type USCoreEthnicityExtension_Extension_DetailedSliceFlatAll = USCoreEthnicityExtension_Extension_DetailedSliceFlat; + export type USCoreEthnicityExtension_Extension_TextSliceFlat = Omit; +export type USCoreEthnicityExtension_Extension_TextSliceFlatAll = USCoreEthnicityExtension_Extension_TextSliceFlat & { + readonly url: "text"; +} import { - buildResource, isRawExtensionInput, applySliceMatch, matchesValue, @@ -19,7 +25,6 @@ import { ensureSliceDefaults, setArraySliceAll, getArraySliceAll, - stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, isExtension, @@ -98,10 +103,10 @@ export class USCoreEthnicityExtensionProfile { static createResource (args: USCoreEthnicityExtensionProfileRaw | USCoreEthnicityExtensionProfileFlat) : Extension { const resolvedExtensions = USCoreEthnicityExtensionProfile.resolveInput(args ?? {}); - const resource = buildResource( { + const resource: Extension = { url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", extension: resolvedExtensions, - }) + } return resource; } @@ -202,36 +207,36 @@ export class USCoreEthnicityExtensionProfile { return this } - public getExtensionOmbCategory(mode: 'flat'): USCoreEthnicityExtension_Extension_OmbCategorySliceFlat | undefined; + public getExtensionOmbCategory(mode: 'flat'): USCoreEthnicityExtension_Extension_OmbCategorySliceFlatAll | undefined; public getExtensionOmbCategory(mode: 'raw'): Extension | undefined; - public getExtensionOmbCategory(): USCoreEthnicityExtension_Extension_OmbCategorySliceFlat | undefined; - public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_OmbCategorySliceFlat | Extension | undefined { + public getExtensionOmbCategory(): USCoreEthnicityExtension_Extension_OmbCategorySliceFlatAll | undefined; + public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_OmbCategorySliceFlatAll | Extension | undefined { const match = USCoreEthnicityExtensionProfile.ombCategorySliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreEthnicityExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed(mode: 'flat'): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll[]; public getExtensionDetailed(mode: 'raw'): Extension[]; - public getExtensionDetailed(): USCoreEthnicityExtension_Extension_DetailedSliceFlat[]; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreEthnicityExtension_Extension_DetailedSliceFlat | Extension)[] { + public getExtensionDetailed(): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll[]; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreEthnicityExtension_Extension_DetailedSliceFlatAll | Extension)[] { const match = USCoreEthnicityExtensionProfile.detailedSliceMatch const items = getArraySliceAll(this.resource.extension, match) if (mode === 'raw') return items - return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) + return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } - public getExtensionText(mode: 'flat'): USCoreEthnicityExtension_Extension_TextSliceFlat | undefined; + public getExtensionText(mode: 'flat'): USCoreEthnicityExtension_Extension_TextSliceFlatAll | undefined; public getExtensionText(mode: 'raw'): Extension | undefined; - public getExtensionText(): USCoreEthnicityExtension_Extension_TextSliceFlat | undefined; - public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_TextSliceFlat | Extension | undefined { + public getExtensionText(): USCoreEthnicityExtension_Extension_TextSliceFlatAll | undefined; + public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreEthnicityExtension_Extension_TextSliceFlatAll | Extension | undefined { const match = USCoreEthnicityExtensionProfile.textSliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return stripMatchKeys(item, ["url"]) + return item as unknown as USCoreEthnicityExtension_Extension_TextSliceFlatAll } // Validation 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 d5afa68d..50d63194 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 @@ -6,11 +6,17 @@ import type { Coding } from "../../hl7-fhir-r4-core/Coding"; import type { Extension } from "../../hl7-fhir-r4-core/Extension"; export type USCoreRaceExtension_Extension_OmbCategorySliceFlat = Omit & Coding; +export type USCoreRaceExtension_Extension_OmbCategorySliceFlatAll = USCoreRaceExtension_Extension_OmbCategorySliceFlat; + export type USCoreRaceExtension_Extension_DetailedSliceFlat = Omit & Coding; +export type USCoreRaceExtension_Extension_DetailedSliceFlatAll = USCoreRaceExtension_Extension_DetailedSliceFlat; + export type USCoreRaceExtension_Extension_TextSliceFlat = Omit; +export type USCoreRaceExtension_Extension_TextSliceFlatAll = USCoreRaceExtension_Extension_TextSliceFlat & { + readonly url: "text"; +} import { - buildResource, isRawExtensionInput, applySliceMatch, matchesValue, @@ -19,7 +25,6 @@ import { ensureSliceDefaults, setArraySliceAll, getArraySliceAll, - stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, isExtension, @@ -98,10 +103,10 @@ export class USCoreRaceExtensionProfile { static createResource (args: USCoreRaceExtensionProfileRaw | USCoreRaceExtensionProfileFlat) : Extension { const resolvedExtensions = USCoreRaceExtensionProfile.resolveInput(args ?? {}); - const resource = buildResource( { + const resource: Extension = { url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", extension: resolvedExtensions, - }) + } return resource; } @@ -202,36 +207,36 @@ export class USCoreRaceExtensionProfile { return this } - public getExtensionOmbCategory(mode: 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlat | undefined; + public getExtensionOmbCategory(mode: 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | undefined; public getExtensionOmbCategory(mode: 'raw'): Extension | undefined; - public getExtensionOmbCategory(): USCoreRaceExtension_Extension_OmbCategorySliceFlat | undefined; - public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlat | Extension | undefined { + public getExtensionOmbCategory(): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | undefined; + public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | Extension | undefined { const match = USCoreRaceExtensionProfile.ombCategorySliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll[]; public getExtensionDetailed(mode: 'raw'): Extension[]; - public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlat[]; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlat | Extension)[] { + public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlatAll[]; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlatAll | Extension)[] { const match = USCoreRaceExtensionProfile.detailedSliceMatch const items = getArraySliceAll(this.resource.extension, match) if (mode === 'raw') return items - return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) + return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } - public getExtensionText(mode: 'flat'): USCoreRaceExtension_Extension_TextSliceFlat | undefined; + public getExtensionText(mode: 'flat'): USCoreRaceExtension_Extension_TextSliceFlatAll | undefined; public getExtensionText(mode: 'raw'): Extension | undefined; - public getExtensionText(): USCoreRaceExtension_Extension_TextSliceFlat | undefined; - public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_TextSliceFlat | Extension | undefined { + public getExtensionText(): USCoreRaceExtension_Extension_TextSliceFlatAll | undefined; + public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_TextSliceFlatAll | Extension | undefined { const match = USCoreRaceExtensionProfile.textSliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return stripMatchKeys(item, ["url"]) + return item as unknown as USCoreRaceExtension_Extension_TextSliceFlatAll } // Validation diff --git a/src/api/writer-generator/typescript/profile-slices.ts b/src/api/writer-generator/typescript/profile-slices.ts index d5fc7e8c..60058e32 100644 --- a/src/api/writer-generator/typescript/profile-slices.ts +++ b/src/api/writer-generator/typescript/profile-slices.ts @@ -256,7 +256,11 @@ export const generateSliceGetters = ( // Implementation w.curlyBlock( - ["public", getMethodName, `(mode: 'flat' | 'raw' = '${defaultMode}'): (${flatTypeName} | ${baseType})[]`], + [ + "public", + getMethodName, + `(mode: 'flat' | 'raw' = '${defaultMode}'): (${flatTypeName} | ${baseType})[]`, + ], () => { w.line(`const match = ${matchRef}`); w.line(`const items = getArraySliceAll(${fieldAccess}, match)`); diff --git a/test/api/write-generator/__snapshots__/typescript.test.ts.snap b/test/api/write-generator/__snapshots__/typescript.test.ts.snap index d29af3cb..c446879f 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -323,16 +323,17 @@ import type { Quantity } from "../../hl7-fhir-r4-core/Quantity"; import type { Reference } from "../../hl7-fhir-r4-core/Reference"; export type Observation_bodyweight_Category_VSCatSliceFlat = Omit; +export type Observation_bodyweight_Category_VSCatSliceFlatAll = Observation_bodyweight_Category_VSCatSliceFlat & { + readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }]; +} import { - buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, - stripMatchKeys, validateRequired, validateExcluded, validateFixedValue, @@ -353,7 +354,9 @@ export type observation_bodyweightProfileRaw = { export class observation_bodyweightProfile { static readonly canonicalUrl = "http://hl7.org/fhir/StructureDefinition/bodyweight"; - private static readonly VSCatSliceMatch: Record = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]}; + private static readonly VSCatSliceMatch: Record = { + "coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}], + } private resource: Observation; @@ -389,14 +392,14 @@ export class observation_bodyweightProfile { observation_bodyweightProfile.VSCatSliceMatch, ); - const resource = buildResource( { + const resource: Observation = { resourceType: "Observation", code: {"coding":[{"code":"29463-7","system":"http://loinc.org"}]}, category: categoryWithDefaults, status: args.status, subject: args.subject, meta: { profile: [observation_bodyweightProfile.canonicalUrl] }, - }) + } return resource; } @@ -481,15 +484,15 @@ export class observation_bodyweightProfile { return this } - public getVSCat(mode: 'flat'): Observation_bodyweight_Category_VSCatSliceFlat | undefined; + public getVSCat(mode: 'flat'): Observation_bodyweight_Category_VSCatSliceFlatAll | undefined; public getVSCat(mode: 'raw'): CodeableConcept | undefined; - public getVSCat(): Observation_bodyweight_Category_VSCatSliceFlat | undefined; - public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bodyweight_Category_VSCatSliceFlat | CodeableConcept | undefined { + public getVSCat(): Observation_bodyweight_Category_VSCatSliceFlatAll | undefined; + public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bodyweight_Category_VSCatSliceFlatAll | CodeableConcept | undefined { const match = observation_bodyweightProfile.VSCatSliceMatch const item = getArraySlice(this.resource.category, match) if (!item) return undefined if (mode === 'raw') return item - return stripMatchKeys(item, ["coding"]) + return item as unknown as Observation_bodyweight_Category_VSCatSliceFlatAll } // Validation @@ -536,18 +539,23 @@ import type { Quantity } from "../../hl7-fhir-r4-core/Quantity"; import type { Reference } from "../../hl7-fhir-r4-core/Reference"; export type Observation_bp_Category_VSCatSliceFlat = Omit; +export type Observation_bp_Category_VSCatSliceFlatAll = Observation_bp_Category_VSCatSliceFlat & { + readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }]; +} + export type Observation_bp_Component_SystolicBPSliceFlat = Omit & Quantity; +export type Observation_bp_Component_SystolicBPSliceFlatAll = Observation_bp_Component_SystolicBPSliceFlat; + export type Observation_bp_Component_DiastolicBPSliceFlat = Omit & Quantity; +export type Observation_bp_Component_DiastolicBPSliceFlatAll = Observation_bp_Component_DiastolicBPSliceFlat; import { - buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, - stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, validateRequired, @@ -571,9 +579,15 @@ export type observation_bpProfileRaw = { export class observation_bpProfile { static readonly canonicalUrl = "http://hl7.org/fhir/StructureDefinition/bp"; - private static readonly VSCatSliceMatch: Record = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]}; - private static readonly SystolicBPSliceMatch: Record = {"code":{"coding":[{"code":"8480-6","system":"http://loinc.org"}]}}; - private static readonly DiastolicBPSliceMatch: Record = {"code":{"coding":[{"code":"8462-4","system":"http://loinc.org"}]}}; + private static readonly VSCatSliceMatch: Record = { + "coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}], + } + private static readonly SystolicBPSliceMatch: Record = { + "code": {"coding":[{"code":"8480-6","system":"http://loinc.org"}]}, + } + private static readonly DiastolicBPSliceMatch: Record = { + "code": {"coding":[{"code":"8462-4","system":"http://loinc.org"}]}, + } private resource: Observation; @@ -619,7 +633,7 @@ export class observation_bpProfile { observation_bpProfile.DiastolicBPSliceMatch, ); - const resource = buildResource( { + const resource: Observation = { resourceType: "Observation", code: {"coding":[{"code":"85354-9","system":"http://loinc.org"}]}, category: categoryWithDefaults, @@ -627,7 +641,7 @@ export class observation_bpProfile { status: args.status, subject: args.subject, meta: { profile: [observation_bpProfile.canonicalUrl] }, - }) + } return resource; } @@ -745,37 +759,37 @@ export class observation_bpProfile { return this } - public getVSCat(mode: 'flat'): Observation_bp_Category_VSCatSliceFlat | undefined; + public getVSCat(mode: 'flat'): Observation_bp_Category_VSCatSliceFlatAll | undefined; public getVSCat(mode: 'raw'): CodeableConcept | undefined; - public getVSCat(): Observation_bp_Category_VSCatSliceFlat | undefined; - public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Category_VSCatSliceFlat | CodeableConcept | undefined { + public getVSCat(): Observation_bp_Category_VSCatSliceFlatAll | undefined; + public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Category_VSCatSliceFlatAll | CodeableConcept | undefined { const match = observation_bpProfile.VSCatSliceMatch const item = getArraySlice(this.resource.category, match) if (!item) return undefined if (mode === 'raw') return item - return stripMatchKeys(item, ["coding"]) + return item as unknown as Observation_bp_Category_VSCatSliceFlatAll } - public getSystolicBP(mode: 'flat'): Observation_bp_Component_SystolicBPSliceFlat | undefined; + public getSystolicBP(mode: 'flat'): Observation_bp_Component_SystolicBPSliceFlatAll | undefined; public getSystolicBP(mode: 'raw'): ObservationComponent | undefined; - public getSystolicBP(): Observation_bp_Component_SystolicBPSliceFlat | undefined; - public getSystolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_SystolicBPSliceFlat | ObservationComponent | undefined { + public getSystolicBP(): Observation_bp_Component_SystolicBPSliceFlatAll | undefined; + public getSystolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_SystolicBPSliceFlatAll | ObservationComponent | undefined { const match = observation_bpProfile.SystolicBPSliceMatch const item = getArraySlice(this.resource.component, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["code"], "valueQuantity") + return unwrapSliceChoice(item, ["code"], "valueQuantity") } - public getDiastolicBP(mode: 'flat'): Observation_bp_Component_DiastolicBPSliceFlat | undefined; + public getDiastolicBP(mode: 'flat'): Observation_bp_Component_DiastolicBPSliceFlatAll | undefined; public getDiastolicBP(mode: 'raw'): ObservationComponent | undefined; - public getDiastolicBP(): Observation_bp_Component_DiastolicBPSliceFlat | undefined; - public getDiastolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_DiastolicBPSliceFlat | ObservationComponent | undefined { + public getDiastolicBP(): Observation_bp_Component_DiastolicBPSliceFlatAll | undefined; + public getDiastolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_DiastolicBPSliceFlatAll | ObservationComponent | undefined { const match = observation_bpProfile.DiastolicBPSliceMatch const item = getArraySlice(this.resource.component, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["code"], "valueQuantity") + return unwrapSliceChoice(item, ["code"], "valueQuantity") } // Validation @@ -837,7 +851,6 @@ import { } from "./Extension_USCoreTribalAffiliationExtension"; import { - buildResource, ensureProfile, extractComplexExtension, isExtension, @@ -885,12 +898,12 @@ export class USCorePatientProfile { } static createResource (args: USCorePatientProfileRaw) : Patient { - const resource = buildResource( { + const resource: Patient = { resourceType: "Patient", identifier: args.identifier, name: args.name, meta: { profile: [USCorePatientProfile.canonicalUrl] }, - }) + } return resource; } @@ -1083,18 +1096,23 @@ import type { Reference } from "../../hl7-fhir-r4-core/Reference"; import type { SampledData } from "../../hl7-fhir-r4-core/SampledData"; export type USCoreBloodPressureProfile_Category_VSCatSliceFlat = Omit; +export type USCoreBloodPressureProfile_Category_VSCatSliceFlatAll = USCoreBloodPressureProfile_Category_VSCatSliceFlat & { + readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }]; +} + export type USCoreBloodPressureProfile_Component_SystolicSliceFlat = Omit & Quantity; +export type USCoreBloodPressureProfile_Component_SystolicSliceFlatAll = USCoreBloodPressureProfile_Component_SystolicSliceFlat; + export type USCoreBloodPressureProfile_Component_DiastolicSliceFlat = Omit & Quantity; +export type USCoreBloodPressureProfile_Component_DiastolicSliceFlatAll = USCoreBloodPressureProfile_Component_DiastolicSliceFlat; import { - buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, - stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, validateRequired, @@ -1118,9 +1136,15 @@ export type USCoreBloodPressureProfileRaw = { export class USCoreBloodPressureProfile { static readonly canonicalUrl = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure"; - private static readonly VSCatSliceMatch: Record = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]}; - private static readonly systolicSliceMatch: Record = {"code":{"coding":[{"system":"http://loinc.org","code":"8480-6"}]}}; - private static readonly diastolicSliceMatch: Record = {"code":{"coding":[{"system":"http://loinc.org","code":"8462-4"}]}}; + private static readonly VSCatSliceMatch: Record = { + "coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}], + } + private static readonly systolicSliceMatch: Record = { + "code": {"coding":[{"system":"http://loinc.org","code":"8480-6"}]}, + } + private static readonly diastolicSliceMatch: Record = { + "code": {"coding":[{"system":"http://loinc.org","code":"8462-4"}]}, + } private resource: Observation; @@ -1166,7 +1190,7 @@ export class USCoreBloodPressureProfile { USCoreBloodPressureProfile.diastolicSliceMatch, ); - const resource = buildResource( { + const resource: Observation = { resourceType: "Observation", code: {"coding":[{"system":"http://loinc.org","code":"85354-9"}]}, category: categoryWithDefaults, @@ -1174,7 +1198,7 @@ export class USCoreBloodPressureProfile { status: args.status, subject: args.subject, meta: { profile: [USCoreBloodPressureProfile.canonicalUrl] }, - }) + } return resource; } @@ -1382,37 +1406,37 @@ export class USCoreBloodPressureProfile { return this } - public getVSCat(mode: 'flat'): USCoreBloodPressureProfile_Category_VSCatSliceFlat | undefined; + public getVSCat(mode: 'flat'): USCoreBloodPressureProfile_Category_VSCatSliceFlatAll | undefined; public getVSCat(mode: 'raw'): CodeableConcept | undefined; - public getVSCat(): USCoreBloodPressureProfile_Category_VSCatSliceFlat | undefined; - public getVSCat (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Category_VSCatSliceFlat | CodeableConcept | undefined { + public getVSCat(): USCoreBloodPressureProfile_Category_VSCatSliceFlatAll | undefined; + public getVSCat (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Category_VSCatSliceFlatAll | CodeableConcept | undefined { const match = USCoreBloodPressureProfile.VSCatSliceMatch const item = getArraySlice(this.resource.category, match) if (!item) return undefined if (mode === 'raw') return item - return stripMatchKeys(item, ["coding"]) + return item as unknown as USCoreBloodPressureProfile_Category_VSCatSliceFlatAll } - public getSystolic(mode: 'flat'): USCoreBloodPressureProfile_Component_SystolicSliceFlat | undefined; + public getSystolic(mode: 'flat'): USCoreBloodPressureProfile_Component_SystolicSliceFlatAll | undefined; public getSystolic(mode: 'raw'): ObservationComponent | undefined; - public getSystolic(): USCoreBloodPressureProfile_Component_SystolicSliceFlat | undefined; - public getSystolic (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Component_SystolicSliceFlat | ObservationComponent | undefined { + public getSystolic(): USCoreBloodPressureProfile_Component_SystolicSliceFlatAll | undefined; + public getSystolic (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Component_SystolicSliceFlatAll | ObservationComponent | undefined { const match = USCoreBloodPressureProfile.systolicSliceMatch const item = getArraySlice(this.resource.component, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["code"], "valueQuantity") + return unwrapSliceChoice(item, ["code"], "valueQuantity") } - public getDiastolic(mode: 'flat'): USCoreBloodPressureProfile_Component_DiastolicSliceFlat | undefined; + public getDiastolic(mode: 'flat'): USCoreBloodPressureProfile_Component_DiastolicSliceFlatAll | undefined; public getDiastolic(mode: 'raw'): ObservationComponent | undefined; - public getDiastolic(): USCoreBloodPressureProfile_Component_DiastolicSliceFlat | undefined; - public getDiastolic (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Component_DiastolicSliceFlat | ObservationComponent | undefined { + public getDiastolic(): USCoreBloodPressureProfile_Component_DiastolicSliceFlatAll | undefined; + public getDiastolic (mode: 'flat' | 'raw' = 'flat'): USCoreBloodPressureProfile_Component_DiastolicSliceFlatAll | ObservationComponent | undefined { const match = USCoreBloodPressureProfile.diastolicSliceMatch const item = getArraySlice(this.resource.component, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["code"], "valueQuantity") + return unwrapSliceChoice(item, ["code"], "valueQuantity") } // Validation @@ -1466,16 +1490,17 @@ import type { Reference } from "../../hl7-fhir-r4-core/Reference"; import type { SampledData } from "../../hl7-fhir-r4-core/SampledData"; export type USCoreBodyWeightProfile_Category_VSCatSliceFlat = Omit; +export type USCoreBodyWeightProfile_Category_VSCatSliceFlatAll = USCoreBodyWeightProfile_Category_VSCatSliceFlat & { + readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }]; +} import { - buildResource, ensureProfile, applySliceMatch, matchesValue, setArraySlice, getArraySlice, ensureSliceDefaults, - stripMatchKeys, validateRequired, validateExcluded, validateFixedValue, @@ -1496,7 +1521,9 @@ export type USCoreBodyWeightProfileRaw = { export class USCoreBodyWeightProfile { static readonly canonicalUrl = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-weight"; - private static readonly VSCatSliceMatch: Record = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]}; + private static readonly VSCatSliceMatch: Record = { + "coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}], + } private resource: Observation; @@ -1532,14 +1559,14 @@ export class USCoreBodyWeightProfile { USCoreBodyWeightProfile.VSCatSliceMatch, ); - const resource = buildResource( { + const resource: Observation = { resourceType: "Observation", code: {"coding":[{"system":"http://loinc.org","code":"29463-7"}]}, category: categoryWithDefaults, status: args.status, subject: args.subject, meta: { profile: [USCoreBodyWeightProfile.canonicalUrl] }, - }) + } return resource; } @@ -1714,15 +1741,15 @@ export class USCoreBodyWeightProfile { return this } - public getVSCat(mode: 'flat'): USCoreBodyWeightProfile_Category_VSCatSliceFlat | undefined; + public getVSCat(mode: 'flat'): USCoreBodyWeightProfile_Category_VSCatSliceFlatAll | undefined; public getVSCat(mode: 'raw'): CodeableConcept | undefined; - public getVSCat(): USCoreBodyWeightProfile_Category_VSCatSliceFlat | undefined; - public getVSCat (mode: 'flat' | 'raw' = 'flat'): USCoreBodyWeightProfile_Category_VSCatSliceFlat | CodeableConcept | undefined { + public getVSCat(): USCoreBodyWeightProfile_Category_VSCatSliceFlatAll | undefined; + public getVSCat (mode: 'flat' | 'raw' = 'flat'): USCoreBodyWeightProfile_Category_VSCatSliceFlatAll | CodeableConcept | undefined { const match = USCoreBodyWeightProfile.VSCatSliceMatch const item = getArraySlice(this.resource.category, match) if (!item) return undefined if (mode === 'raw') return item - return stripMatchKeys(item, ["coding"]) + return item as unknown as USCoreBodyWeightProfile_Category_VSCatSliceFlatAll } // Validation @@ -1778,11 +1805,17 @@ import type { Coding } from "../../hl7-fhir-r4-core/Coding"; import type { Extension } from "../../hl7-fhir-r4-core/Extension"; export type USCoreRaceExtension_Extension_OmbCategorySliceFlat = Omit & Coding; +export type USCoreRaceExtension_Extension_OmbCategorySliceFlatAll = USCoreRaceExtension_Extension_OmbCategorySliceFlat; + export type USCoreRaceExtension_Extension_DetailedSliceFlat = Omit & Coding; +export type USCoreRaceExtension_Extension_DetailedSliceFlatAll = USCoreRaceExtension_Extension_DetailedSliceFlat; + export type USCoreRaceExtension_Extension_TextSliceFlat = Omit; +export type USCoreRaceExtension_Extension_TextSliceFlatAll = USCoreRaceExtension_Extension_TextSliceFlat & { + readonly url: "text"; +} import { - buildResource, isRawExtensionInput, applySliceMatch, matchesValue, @@ -1791,7 +1824,6 @@ import { ensureSliceDefaults, setArraySliceAll, getArraySliceAll, - stripMatchKeys, wrapSliceChoice, unwrapSliceChoice, isExtension, @@ -1870,10 +1902,10 @@ export class USCoreRaceExtensionProfile { static createResource (args: USCoreRaceExtensionProfileRaw | USCoreRaceExtensionProfileFlat) : Extension { const resolvedExtensions = USCoreRaceExtensionProfile.resolveInput(args ?? {}); - const resource = buildResource( { + const resource: Extension = { url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", extension: resolvedExtensions, - }) + } return resource; } @@ -1974,36 +2006,36 @@ export class USCoreRaceExtensionProfile { return this } - public getExtensionOmbCategory(mode: 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlat | undefined; + public getExtensionOmbCategory(mode: 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | undefined; public getExtensionOmbCategory(mode: 'raw'): Extension | undefined; - public getExtensionOmbCategory(): USCoreRaceExtension_Extension_OmbCategorySliceFlat | undefined; - public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlat | Extension | undefined { + public getExtensionOmbCategory(): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | undefined; + public getExtensionOmbCategory (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_OmbCategorySliceFlatAll | Extension | undefined { const match = USCoreRaceExtensionProfile.ombCategorySliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return unwrapSliceChoice(item, ["url"], "valueCoding") + return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlat[]; + public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll[]; public getExtensionDetailed(mode: 'raw'): Extension[]; - public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlat[]; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlat | Extension)[] { + public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlatAll[]; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlatAll | Extension)[] { const match = USCoreRaceExtensionProfile.detailedSliceMatch const items = getArraySliceAll(this.resource.extension, match) if (mode === 'raw') return items - return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) + return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } - public getExtensionText(mode: 'flat'): USCoreRaceExtension_Extension_TextSliceFlat | undefined; + public getExtensionText(mode: 'flat'): USCoreRaceExtension_Extension_TextSliceFlatAll | undefined; public getExtensionText(mode: 'raw'): Extension | undefined; - public getExtensionText(): USCoreRaceExtension_Extension_TextSliceFlat | undefined; - public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_TextSliceFlat | Extension | undefined { + public getExtensionText(): USCoreRaceExtension_Extension_TextSliceFlatAll | undefined; + public getExtensionText (mode: 'flat' | 'raw' = 'flat'): USCoreRaceExtension_Extension_TextSliceFlatAll | Extension | undefined { const match = USCoreRaceExtensionProfile.textSliceMatch const item = getArraySlice(this.resource.extension, match) if (!item) return undefined if (mode === 'raw') return item - return stripMatchKeys(item, ["url"]) + return item as unknown as USCoreRaceExtension_Extension_TextSliceFlatAll } // Validation diff --git a/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap index a360cc58..0b0b856a 100644 --- a/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap +++ b/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap @@ -38,10 +38,12 @@ import type { Organization } from "../../hl7-fhir-r4-core/Organization"; import type { Patient } from "../../hl7-fhir-r4-core/Patient"; export type ExampleTypedBundle_Entry_PatientEntrySliceFlat = BundleEntry; +export type ExampleTypedBundle_Entry_PatientEntrySliceFlatAll = ExampleTypedBundle_Entry_PatientEntrySliceFlat; + export type ExampleTypedBundle_Entry_OrganizationEntrySliceFlat = BundleEntry; +export type ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll = ExampleTypedBundle_Entry_OrganizationEntrySliceFlat; import { - buildResource, ensureProfile, applySliceMatch, matchesValue, @@ -69,7 +71,9 @@ export class ExampleTypedBundleProfile { static readonly canonicalUrl = "http://example.org/fhir/StructureDefinition/ExampleTypedBundle"; private static readonly PatientEntrySliceMatch: Record = {"resource":{"resourceType":"Patient"}}; - private static readonly OrganizationEntrySliceMatch: Record = {"resource":{"resourceType":"Organization"}}; + private static readonly OrganizationEntrySliceMatch: Record = { + "resource": {"resourceType":"Organization"}, + } private resource: Bundle; @@ -93,11 +97,11 @@ export class ExampleTypedBundleProfile { } static createResource (args: ExampleTypedBundleProfileRaw) : Bundle { - const resource = buildResource( { + const resource: Bundle = { resourceType: "Bundle", type: args.type, meta: { profile: [ExampleTypedBundleProfile.canonicalUrl] }, - }) + } return resource; } @@ -141,25 +145,25 @@ export class ExampleTypedBundleProfile { return this } - public getPatientEntry(mode: 'flat'): ExampleTypedBundle_Entry_PatientEntrySliceFlat | undefined; + public getPatientEntry(mode: 'flat'): ExampleTypedBundle_Entry_PatientEntrySliceFlatAll | undefined; public getPatientEntry(mode: 'raw'): BundleEntry | undefined; - public getPatientEntry(): ExampleTypedBundle_Entry_PatientEntrySliceFlat | undefined; - public getPatientEntry (mode: 'flat' | 'raw' = 'flat'): ExampleTypedBundle_Entry_PatientEntrySliceFlat | BundleEntry | undefined { + public getPatientEntry(): ExampleTypedBundle_Entry_PatientEntrySliceFlatAll | undefined; + public getPatientEntry (mode: 'flat' | 'raw' = 'flat'): ExampleTypedBundle_Entry_PatientEntrySliceFlatAll | BundleEntry | undefined { const match = ExampleTypedBundleProfile.PatientEntrySliceMatch const item = getArraySlice(this.resource.entry, match) if (!item) return undefined if (mode === 'raw') return item as BundleEntry - return item as ExampleTypedBundle_Entry_PatientEntrySliceFlat + return item as unknown as ExampleTypedBundle_Entry_PatientEntrySliceFlatAll } - public getOrganizationEntry(mode: 'flat'): ExampleTypedBundle_Entry_OrganizationEntrySliceFlat[]; + public getOrganizationEntry(mode: 'flat'): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll[]; public getOrganizationEntry(mode: 'raw'): BundleEntry[]; - public getOrganizationEntry(): ExampleTypedBundle_Entry_OrganizationEntrySliceFlat[]; - public getOrganizationEntry (mode: 'flat' | 'raw' = 'flat'): (ExampleTypedBundle_Entry_OrganizationEntrySliceFlat | BundleEntry)[] { + public getOrganizationEntry(): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll[]; + public getOrganizationEntry (mode: 'flat' | 'raw' = 'flat'): (ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll | BundleEntry)[] { const match = ExampleTypedBundleProfile.OrganizationEntrySliceMatch const items = getArraySliceAll(this.resource.entry, match) if (mode === 'raw') return items as BundleEntry[] - return items as ExampleTypedBundle_Entry_OrganizationEntrySliceFlat[] + return items as unknown as ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll[] } // Validation From 70b448058b517d59303d710b32e91e519d6e0efd Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 6 Apr 2026 22:52:08 +0200 Subject: [PATCH 5/7] test: Rewrite typed bundle tests with demo-first structure --- .../profile-typed-bundle.test.ts | 105 ++++++++++++------ 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/examples/local-package-folder/profile-typed-bundle.test.ts b/examples/local-package-folder/profile-typed-bundle.test.ts index 5753633b..e069063d 100644 --- a/examples/local-package-folder/profile-typed-bundle.test.ts +++ b/examples/local-package-folder/profile-typed-bundle.test.ts @@ -17,78 +17,111 @@ import type { Patient } from "./fhir-types/hl7-fhir-r4-core/Patient"; const smithPatient: Patient = { resourceType: "Patient", name: [{ family: "Smith" }] }; const activePatient: Patient = { resourceType: "Patient", active: true }; const clinicOrg: Organization = { resourceType: "Organization", name: "Clinic" }; +const acmeOrg: Organization = { resourceType: "Organization", name: "Acme" }; -describe("type-discriminated bundle slices", () => { - test("create() starts with no entry — PatientEntry must be set by user", () => { +describe("demo: single-element slice (max: 1) — PatientEntry", () => { + test("create, set, and validate a typed patient entry", () => { const bundle = ExampleTypedBundleProfile.create({ type: "collection" }); - expect(bundle.toResource().entry).toBeUndefined(); + + // No entry yet — validation fails (PatientEntry min: 1) expect(bundle.validate().errors).toEqual([ "ExampleTypedBundle.entry: slice 'PatientEntry' requires at least 1 item(s), found 0", ]); - }); - test("setPatientEntry inserts a typed patient entry", () => { - const bundle = ExampleTypedBundleProfile.create({ type: "collection" }); + // Set a single patient entry — typed as BundleEntry bundle.setPatientEntry({ resource: smithPatient }); + expect(bundle.validate().errors).toEqual([]); + // Getter returns the entry with resource narrowed to Patient const entry = bundle.getPatientEntry()!; expect(entry.resource).toEqual(smithPatient); - expect(bundle.validate().errors).toEqual([]); }); - test("setPatientEntry replaces existing patient entry (no duplicates)", () => { + test("setPatientEntry replaces existing entry (no duplicates)", () => { const bundle = ExampleTypedBundleProfile.create({ type: "collection" }); bundle.setPatientEntry({ resource: smithPatient }); bundle.setPatientEntry({ resource: activePatient }); - const entries = bundle.toResource().entry!; - expect(entries).toHaveLength(1); - expect(entries[0]!.resource).toEqual(activePatient); + // Only one patient entry — the second call replaced the first + expect(bundle.toResource().entry).toHaveLength(1); + expect(bundle.getPatientEntry()!.resource).toEqual(activePatient); }); +}); - test("getPatientEntry('flat') returns the entry as-is (no keys stripped)", () => { - const bundle = ExampleTypedBundleProfile.create({ type: "collection" }); - bundle.setPatientEntry({ fullUrl: "urn:uuid:patient-1", resource: activePatient }); +describe("demo: unbounded slice (max: *) — OrganizationEntry", () => { + test("setter accepts an array, getter returns an array", () => { + const bundle = ExampleTypedBundleProfile.create({ type: "collection" }).setPatientEntry({ + resource: activePatient, + }); - const flat = bundle.getPatientEntry("flat")!; - expect(flat.fullUrl).toBe("urn:uuid:patient-1"); - expect(flat.resource).toEqual(activePatient); + // Set multiple organization entries at once + bundle.setOrganizationEntry([{ resource: clinicOrg }, { resource: acmeOrg }]); + + // Getter returns all matching entries as an array + const orgs = bundle.getOrganizationEntry(); + expect(orgs).toHaveLength(2); + expect(orgs[0]!.resource).toEqual(clinicOrg); + expect(orgs[1]!.resource).toEqual(acmeOrg); + + // Total entries: 1 patient + 2 organizations + expect(bundle.toResource().entry).toHaveLength(3); }); - test("fluent chaining across slice setters", () => { + test("setOrganizationEntry replaces all previous org entries", () => { const bundle = ExampleTypedBundleProfile.create({ type: "collection" }) .setPatientEntry({ resource: activePatient }) - .setOrganizationEntry([{ resource: clinicOrg }]); + .setOrganizationEntry([{ resource: clinicOrg }, { resource: acmeOrg }]); + + // Replace with a single org — previous two are removed + bundle.setOrganizationEntry([{ resource: { resourceType: "Organization", name: "NewCo" } }]); + + const orgs = bundle.getOrganizationEntry(); + expect(orgs).toHaveLength(1); + expect(orgs[0]!.resource!.name).toBe("NewCo"); - expect(bundle.toResource().entry).toHaveLength(2); + // Patient entry unaffected expect(bundle.getPatientEntry()!.resource).toEqual(activePatient); - expect(bundle.getOrganizationEntry()[0]!.resource).toEqual(clinicOrg); }); - test("setOrganizationEntry replaces all org entries (unbounded slice)", () => { + test("append to existing entries via spread", () => { const bundle = ExampleTypedBundleProfile.create({ type: "collection" }) .setPatientEntry({ resource: activePatient }) - .setOrganizationEntry([{ resource: clinicOrg }]) - .setOrganizationEntry([{ resource: { resourceType: "Organization", name: "Acme" } }]); + .setOrganizationEntry([{ resource: clinicOrg }]); - const entries = bundle.toResource().entry!; - expect(entries).toHaveLength(2); - expect(bundle.getOrganizationEntry()[0]!.resource!.name).toBe("Acme"); + // Append a new org by spreading existing entries + bundle.setOrganizationEntry([...bundle.getOrganizationEntry(), { resource: acmeOrg }]); + + const orgs = bundle.getOrganizationEntry(); + expect(orgs).toHaveLength(2); + expect(orgs[0]!.resource).toEqual(clinicOrg); + expect(orgs[1]!.resource).toEqual(acmeOrg); }); - test("setOrganizationEntry supports multiple items (max: *)", () => { + test("empty array removes all org entries", () => { const bundle = ExampleTypedBundleProfile.create({ type: "collection" }) .setPatientEntry({ resource: activePatient }) + .setOrganizationEntry([{ resource: clinicOrg }]); + + bundle.setOrganizationEntry([]); + + expect(bundle.getOrganizationEntry()).toHaveLength(0); + // Patient entry still present + expect(bundle.toResource().entry).toHaveLength(1); + }); +}); + +describe("fluent chaining across slice types", () => { + test("chain single and array setters", () => { + const bundle = ExampleTypedBundleProfile.create({ type: "collection" }) + .setPatientEntry({ fullUrl: "urn:uuid:patient-1", resource: activePatient }) .setOrganizationEntry([ - { resource: clinicOrg }, - { resource: { resourceType: "Organization", name: "Acme" } }, + { fullUrl: "urn:uuid:org-1", resource: clinicOrg }, + { fullUrl: "urn:uuid:org-2", resource: acmeOrg }, ]); - const entries = bundle.toResource().entry!; - expect(entries).toHaveLength(3); - const orgs = bundle.getOrganizationEntry(); - expect(orgs).toHaveLength(2); - expect(orgs[0]!.resource).toEqual(clinicOrg); - expect(orgs[1]!.resource!.name).toBe("Acme"); + expect(bundle.toResource().entry).toHaveLength(3); + expect(bundle.getPatientEntry()!.fullUrl).toBe("urn:uuid:patient-1"); + expect(bundle.getOrganizationEntry()[0]!.fullUrl).toBe("urn:uuid:org-1"); + expect(bundle.getOrganizationEntry()[1]!.fullUrl).toBe("urn:uuid:org-2"); }); }); From 941b47c6fc8dd47b329326fc685272bb6f6f5a8b Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 6 Apr 2026 23:02:04 +0200 Subject: [PATCH 6/7] fix: Unbounded slice getter returns undefined instead of empty array --- src/api/writer-generator/typescript/profile-slices.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/api/writer-generator/typescript/profile-slices.ts b/src/api/writer-generator/typescript/profile-slices.ts index 60058e32..655c1c4b 100644 --- a/src/api/writer-generator/typescript/profile-slices.ts +++ b/src/api/writer-generator/typescript/profile-slices.ts @@ -246,24 +246,25 @@ export const generateSliceGetters = ( const isUnbounded = sliceDef.array && (sliceDef.max === 0 || sliceDef.max === undefined); if (isUnbounded) { - // Unbounded slice: return an array + // Unbounded slice: return an array or undefined const defaultReturn = defaultMode === "raw" ? `${baseType}[]` : `${flatTypeName}[]`; // Overload signatures - w.lineSM(`public ${getMethodName}(mode: 'flat'): ${flatTypeName}[]`); - w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType}[]`); - w.lineSM(`public ${getMethodName}(): ${defaultReturn}`); + w.lineSM(`public ${getMethodName}(mode: 'flat'): ${flatTypeName}[] | undefined`); + w.lineSM(`public ${getMethodName}(mode: 'raw'): ${baseType}[] | undefined`); + w.lineSM(`public ${getMethodName}(): ${defaultReturn} | undefined`); // Implementation w.curlyBlock( [ "public", getMethodName, - `(mode: 'flat' | 'raw' = '${defaultMode}'): (${flatTypeName} | ${baseType})[]`, + `(mode: 'flat' | 'raw' = '${defaultMode}'): (${flatTypeName} | ${baseType})[] | undefined`, ], () => { w.line(`const match = ${matchRef}`); w.line(`const items = getArraySliceAll(${fieldAccess}, match)`); + w.line("if (items.length === 0) return undefined"); if (sliceDef.typeDiscriminator) { w.line(`if (mode === 'raw') return items as ${baseType}[]`); } else { From 7370f720237c4ebf762ee588cd7bda796b9fe7b0 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 6 Apr 2026 23:02:04 +0200 Subject: [PATCH 7/7] chore: Update snapshots and tests for undefined unbounded getter --- .../profile-typed-bundle.test.ts | 19 ++++++++++--------- .../Extension_USCoreEthnicityExtension.ts | 9 +++++---- .../profiles/Extension_USCoreRaceExtension.ts | 9 +++++---- .../__snapshots__/typescript.test.ts.snap | 9 +++++---- .../__snapshots__/local-package.test.ts.snap | 9 +++++---- 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/examples/local-package-folder/profile-typed-bundle.test.ts b/examples/local-package-folder/profile-typed-bundle.test.ts index e069063d..02aa258a 100644 --- a/examples/local-package-folder/profile-typed-bundle.test.ts +++ b/examples/local-package-folder/profile-typed-bundle.test.ts @@ -57,8 +57,8 @@ describe("demo: unbounded slice (max: *) — OrganizationEntry", () => { // Set multiple organization entries at once bundle.setOrganizationEntry([{ resource: clinicOrg }, { resource: acmeOrg }]); - // Getter returns all matching entries as an array - const orgs = bundle.getOrganizationEntry(); + // Getter returns all matching entries as an array (undefined if none) + const orgs = bundle.getOrganizationEntry()!; expect(orgs).toHaveLength(2); expect(orgs[0]!.resource).toEqual(clinicOrg); expect(orgs[1]!.resource).toEqual(acmeOrg); @@ -75,7 +75,7 @@ describe("demo: unbounded slice (max: *) — OrganizationEntry", () => { // Replace with a single org — previous two are removed bundle.setOrganizationEntry([{ resource: { resourceType: "Organization", name: "NewCo" } }]); - const orgs = bundle.getOrganizationEntry(); + const orgs = bundle.getOrganizationEntry()!; expect(orgs).toHaveLength(1); expect(orgs[0]!.resource!.name).toBe("NewCo"); @@ -89,22 +89,23 @@ describe("demo: unbounded slice (max: *) — OrganizationEntry", () => { .setOrganizationEntry([{ resource: clinicOrg }]); // Append a new org by spreading existing entries - bundle.setOrganizationEntry([...bundle.getOrganizationEntry(), { resource: acmeOrg }]); + bundle.setOrganizationEntry([...(bundle.getOrganizationEntry() ?? []), { resource: acmeOrg }]); - const orgs = bundle.getOrganizationEntry(); + const orgs = bundle.getOrganizationEntry()!; expect(orgs).toHaveLength(2); expect(orgs[0]!.resource).toEqual(clinicOrg); expect(orgs[1]!.resource).toEqual(acmeOrg); }); - test("empty array removes all org entries", () => { + test("empty array removes all org entries, getter returns undefined", () => { const bundle = ExampleTypedBundleProfile.create({ type: "collection" }) .setPatientEntry({ resource: activePatient }) .setOrganizationEntry([{ resource: clinicOrg }]); bundle.setOrganizationEntry([]); - expect(bundle.getOrganizationEntry()).toHaveLength(0); + // No matching entries — returns undefined, not empty array + expect(bundle.getOrganizationEntry()).toBeUndefined(); // Patient entry still present expect(bundle.toResource().entry).toHaveLength(1); }); @@ -121,7 +122,7 @@ describe("fluent chaining across slice types", () => { expect(bundle.toResource().entry).toHaveLength(3); expect(bundle.getPatientEntry()!.fullUrl).toBe("urn:uuid:patient-1"); - expect(bundle.getOrganizationEntry()[0]!.fullUrl).toBe("urn:uuid:org-1"); - expect(bundle.getOrganizationEntry()[1]!.fullUrl).toBe("urn:uuid:org-2"); + expect(bundle.getOrganizationEntry()![0]!.fullUrl).toBe("urn:uuid:org-1"); + expect(bundle.getOrganizationEntry()![1]!.fullUrl).toBe("urn:uuid:org-2"); }); }); 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 eb0dca69..cf40c2ef 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 @@ -218,12 +218,13 @@ export class USCoreEthnicityExtensionProfile { return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll[]; - public getExtensionDetailed(mode: 'raw'): Extension[]; - public getExtensionDetailed(): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll[]; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreEthnicityExtension_Extension_DetailedSliceFlatAll | Extension)[] { + public getExtensionDetailed(mode: 'flat'): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll[] | undefined; + public getExtensionDetailed(mode: 'raw'): Extension[] | undefined; + public getExtensionDetailed(): USCoreEthnicityExtension_Extension_DetailedSliceFlatAll[] | undefined; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreEthnicityExtension_Extension_DetailedSliceFlatAll | Extension)[] | undefined { const match = USCoreEthnicityExtensionProfile.detailedSliceMatch const items = getArraySliceAll(this.resource.extension, match) + if (items.length === 0) return undefined if (mode === 'raw') return items return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } 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 50d63194..a6e85b68 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 @@ -218,12 +218,13 @@ export class USCoreRaceExtensionProfile { return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll[]; - public getExtensionDetailed(mode: 'raw'): Extension[]; - public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlatAll[]; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlatAll | Extension)[] { + public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll[] | undefined; + public getExtensionDetailed(mode: 'raw'): Extension[] | undefined; + public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlatAll[] | undefined; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlatAll | Extension)[] | undefined { const match = USCoreRaceExtensionProfile.detailedSliceMatch const items = getArraySliceAll(this.resource.extension, match) + if (items.length === 0) return undefined if (mode === 'raw') return items return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } diff --git a/test/api/write-generator/__snapshots__/typescript.test.ts.snap b/test/api/write-generator/__snapshots__/typescript.test.ts.snap index c446879f..a77ca9ab 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -2017,12 +2017,13 @@ export class USCoreRaceExtensionProfile { return unwrapSliceChoice(item, ["url"], "valueCoding") } - public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll[]; - public getExtensionDetailed(mode: 'raw'): Extension[]; - public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlatAll[]; - public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlatAll | Extension)[] { + public getExtensionDetailed(mode: 'flat'): USCoreRaceExtension_Extension_DetailedSliceFlatAll[] | undefined; + public getExtensionDetailed(mode: 'raw'): Extension[] | undefined; + public getExtensionDetailed(): USCoreRaceExtension_Extension_DetailedSliceFlatAll[] | undefined; + public getExtensionDetailed (mode: 'flat' | 'raw' = 'flat'): (USCoreRaceExtension_Extension_DetailedSliceFlatAll | Extension)[] | undefined { const match = USCoreRaceExtensionProfile.detailedSliceMatch const items = getArraySliceAll(this.resource.extension, match) + if (items.length === 0) return undefined if (mode === 'raw') return items return items.map(item => unwrapSliceChoice(item, ["url"], "valueCoding")) } diff --git a/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap b/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap index 0b0b856a..1418558c 100644 --- a/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap +++ b/test/api/write-generator/multi-package/__snapshots__/local-package.test.ts.snap @@ -156,12 +156,13 @@ export class ExampleTypedBundleProfile { return item as unknown as ExampleTypedBundle_Entry_PatientEntrySliceFlatAll } - public getOrganizationEntry(mode: 'flat'): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll[]; - public getOrganizationEntry(mode: 'raw'): BundleEntry[]; - public getOrganizationEntry(): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll[]; - public getOrganizationEntry (mode: 'flat' | 'raw' = 'flat'): (ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll | BundleEntry)[] { + public getOrganizationEntry(mode: 'flat'): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll[] | undefined; + public getOrganizationEntry(mode: 'raw'): BundleEntry[] | undefined; + public getOrganizationEntry(): ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll[] | undefined; + public getOrganizationEntry (mode: 'flat' | 'raw' = 'flat'): (ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll | BundleEntry)[] | undefined { const match = ExampleTypedBundleProfile.OrganizationEntrySliceMatch const items = getArraySliceAll(this.resource.entry, match) + if (items.length === 0) return undefined if (mode === 'raw') return items as BundleEntry[] return items as unknown as ExampleTypedBundle_Entry_OrganizationEntrySliceFlatAll[] }