From c6b68dd94ee60353fc77e7b564af475796e48c7d Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 3 Jun 2026 10:21:59 +0200 Subject: [PATCH 1/3] fix: enforce choice[x] mutual exclusion in TS profile setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A polymorphic value[x]/effective[x] field permits at most one variant, but each generated choice setter assigned independently — leaving two variants set and serialising an invalid FHIR resource. Choice setters now clear their sibling variants before assigning, via a public clearX() helper generated once per multi-variant choice group. Siblings come from every choice instance sharing a choiceOf (not the declaration's possibly-narrowed `choices` list), so mutual exclusion also holds for profiles that constrain value[x]. Single-variant groups emit no helper. --- .../writer-generator/typescript/profile.ts | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/api/writer-generator/typescript/profile.ts b/src/api/writer-generator/typescript/profile.ts index 6006ef04..ba21343c 100644 --- a/src/api/writer-generator/typescript/profile.ts +++ b/src/api/writer-generator/typescript/profile.ts @@ -51,24 +51,53 @@ type ProfileFactoryInfo = { /** Array fields with required slices — optional param with auto-merge of required stubs */ sliceAutoFields: { name: string; tsType: string; typeId: TypeIdentifier; sliceNames: string[] }[]; params: { name: string; tsType: string; typeId: TypeIdentifier }[]; - accessors: { name: string; tsType: string; typeId: TypeIdentifier }[]; + /** `choiceClearMethod` names the helper a choice setter calls to clear its sibling variants. */ + accessors: { name: string; tsType: string; typeId: TypeIdentifier; choiceClearMethod?: string }[]; + /** One helper per multi-variant choice group: clears every variant of that choice. */ + choiceClearMethods: { method: string; variants: string[] }[]; /** Accessor names that come from valueConstraint fields — skip generating setters for these */ fixedFields: Set; }; +/** Public helper name that clears all variants of a choice group, e.g. `clearValue`. */ +const choiceClearMethodName = (choiceOf: string): string => `clear${uppercaseFirstLetter(tsCamelCase(choiceOf))}`; + const collectChoiceAccessors = ( snapshot: SnapshotProfileTypeSchema, promotedChoices: Set, -): ProfileFactoryInfo["accessors"] => { +): Pick => { + // Group every choice-instance field by its declaration (choiceOf). A profile + // that constrains value[x] narrows the declaration's `choices` list, but the + // base variants still exist as instance fields with generated setters — so + // siblings must come from the instances, not the (possibly narrowed) `choices`. + const variantsByChoice: Record = {}; + for (const [name, field] of Object.entries(snapshot.fields)) { + if (field.excluded) continue; + if (!isChoiceInstanceField(field)) continue; + (variantsByChoice[field.choiceOf] ??= []).push(name); + } + const accessors: ProfileFactoryInfo["accessors"] = []; for (const [name, field] of Object.entries(snapshot.fields)) { if (field.excluded) continue; if (!isChoiceInstanceField(field)) continue; if (promotedChoices.has(name)) continue; const tsType = tsTypeFromIdentifier(field.type) + (field.array ? "[]" : ""); - accessors.push({ name, tsType, typeId: field.type }); + // value[x] variants are mutually exclusive — a single-variant group has + // nothing to clear, so only multi-variant groups get a clear helper. + const hasSiblings = (variantsByChoice[field.choiceOf]?.length ?? 0) > 1; + const choiceClearMethod = hasSiblings ? choiceClearMethodName(field.choiceOf) : undefined; + accessors.push({ name, tsType, typeId: field.type, choiceClearMethod }); } - return accessors; + + const choiceClearMethods = Object.entries(variantsByChoice) + .filter(([, variants]) => variants.length > 1) + .map(([choiceOf, variants]) => ({ + method: choiceClearMethodName(choiceOf), + variants: variants.map(tsFieldName), + })); + + return { accessors, choiceClearMethods }; }; /** Try to promote a required single-choice declaration to a direct param */ @@ -173,8 +202,9 @@ export const collectProfileFactoryInfo = ( isFamilyType, ); - const accessors = [...autoAccessors, ...collectChoiceAccessors(snapshot, promotedChoices)]; - return { autoFields, sliceAutoFields, params, accessors, fixedFields }; + const { accessors: choiceAccessors, choiceClearMethods } = collectChoiceAccessors(snapshot, promotedChoices); + const accessors = [...autoAccessors, ...choiceAccessors]; + return { autoFields, sliceAutoFields, params, accessors, choiceClearMethods, fixedFields }; }; /** Include base-type required fields not already covered by profile constraints */ @@ -618,12 +648,20 @@ const generateFieldAccessors = (w: TypeScript, factoryInfo: ProfileFactoryInfo) w.line(); if (!factoryInfo.fixedFields.has(a.name)) { w.curlyBlock([`set${methodBaseName}`, `(value: ${a.tsType})`, ": this"], () => { + if (a.choiceClearMethod) w.lineSM(`this.${a.choiceClearMethod}()`); w.lineSM(`Object.assign(this.resource, { ${fieldAccess}: value })`); w.lineSM("return this"); }); w.line(); } } + + for (const { method, variants } of factoryInfo.choiceClearMethods) { + w.curlyBlock([method, "()", ": void"], () => { + for (const variant of variants) w.lineSM(`delete ${tsGet("this.resource", variant)}`); + }); + w.line(); + } }; /** Generate inline extension input types only for complex extensions without a resolved FlatInput profile */ From f81e57017bac81d410a46cd88c9c8e7eae070b3d Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 3 Jun 2026 10:22:00 +0200 Subject: [PATCH 2/3] test: cover choice[x] mutual exclusion and the public clearX() helper - value[x] on US Core BP (unconstrained, multi-variant) - effective[x] on bodyweight (reworked from "independent" to exclusive) - value[x] on US Core body weight (constrained declaration) - public clearEffective() helper --- .../typescript-r4/profile-bodyweight.test.ts | 25 +++++++++++++++++- .../profile-bodyweight.test.ts | 26 +++++++++++++++++++ .../typescript-us-core/profile-bp.test.ts | 24 +++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/examples/typescript-r4/profile-bodyweight.test.ts b/examples/typescript-r4/profile-bodyweight.test.ts index 8c2885c9..15310365 100644 --- a/examples/typescript-r4/profile-bodyweight.test.ts +++ b/examples/typescript-r4/profile-bodyweight.test.ts @@ -219,7 +219,7 @@ describe("slice accessors", () => { }); describe("choice type accessors", () => { - test("effectiveDateTime and effectivePeriod are independent choices", () => { + test("setting a second effective[x] choice clears the first", () => { const profile = bodyweightProfile.create({ status: "final", subject: { reference: "Patient/pt-1" } }); expect(profile.getEffectiveDateTime()).toBeUndefined(); @@ -228,7 +228,30 @@ describe("choice type accessors", () => { profile.setEffectiveDateTime("2024-01-15"); expect(profile.getEffectiveDateTime()).toBe("2024-01-15"); + // effective[x] is polymorphic: a resource may carry at most one variant, + // so switching to a different one must clear the previous value — + // otherwise the resource serialises two mutually-exclusive effective* + // fields and is invalid FHIR. profile.setEffectivePeriod({ start: "2024-01-15", end: "2024-01-16" }); expect(profile.getEffectivePeriod()).toEqual({ start: "2024-01-15", end: "2024-01-16" }); + expect(profile.getEffectiveDateTime()).toBeUndefined(); + + const resource = profile.toResource(); + const effectiveKeys = Object.keys(resource).filter((k) => k.startsWith("effective")); + expect(effectiveKeys).toEqual(["effectivePeriod"]); + }); + + test("clearEffective() removes whichever effective[x] variant is set", () => { + const profile = bodyweightProfile.create({ status: "final", subject: { reference: "Patient/pt-1" } }); + + profile.setEffectiveDateTime("2024-01-15"); + expect(profile.getEffectiveDateTime()).toBe("2024-01-15"); + + // The clear helper is public — callers can drop the choice entirely. + profile.clearEffective(); + + expect(profile.getEffectiveDateTime()).toBeUndefined(); + expect(profile.getEffectivePeriod()).toBeUndefined(); + expect(Object.keys(profile.toResource()).some((k) => k.startsWith("effective"))).toBe(false); }); }); diff --git a/examples/typescript-us-core/profile-bodyweight.test.ts b/examples/typescript-us-core/profile-bodyweight.test.ts index 886c5eeb..af6e7fc5 100644 --- a/examples/typescript-us-core/profile-bodyweight.test.ts +++ b/examples/typescript-us-core/profile-bodyweight.test.ts @@ -151,3 +151,29 @@ describe("validation", () => { expect(errors).toContain("USCoreBodyWeightProfile: field 'valueString' must not be present"); }); }); + +describe("value[x] choice is mutually exclusive on a constrained profile", () => { + test("switching variants clears the previous one, even when the declaration narrows value[x]", () => { + // Body weight constrains value[x] to valueQuantity, so the choice + // declaration lists only that variant — but the base value* instances + // still have setters. Mutual exclusion must hold across all of them, + // not just the single declared variant. + const profile = USCoreBodyWeightProfile.create({ + status: "final", + subject: { reference: "Patient/pt-1" }, + }); + + // Two variants that are NOT the declared one — neither cleared the other + // before the fix (siblings were derived from the narrowed declaration). + profile.setValueString("first"); + profile.setValueCodeableConcept({ text: "second" }); + expect(profile.getValueString()).toBeUndefined(); + expect(profile.getValueCodeableConcept()).toEqual({ text: "second" }); + + // The declared variant clears the others too. + profile.setValueQuantity({ value: 70, unit: "kg" }); + const resource = profile.toResource(); + const valueKeys = Object.keys(resource).filter((k) => k.startsWith("value")); + expect(valueKeys).toEqual(["valueQuantity"]); + }); +}); diff --git a/examples/typescript-us-core/profile-bp.test.ts b/examples/typescript-us-core/profile-bp.test.ts index 30c68abf..3d9459b1 100644 --- a/examples/typescript-us-core/profile-bp.test.ts +++ b/examples/typescript-us-core/profile-bp.test.ts @@ -217,3 +217,27 @@ describe("category auto-population", () => { expect(profile.toResource().category).toHaveLength(1); }); }); + +describe("value[x] choice is mutually exclusive", () => { + test("setting a second value variant clears the first", () => { + const profile = USCoreBloodPressureProfile.create({ + status: "final", + subject: { reference: "Patient/pt-1" }, + }); + + profile.setValueQuantity({ value: 120, unit: "mmHg" }); + expect(profile.getValueQuantity()).toEqual({ value: 120, unit: "mmHg" }); + + // A resource may carry at most one value[x] variant — switching to + // valueCodeableConcept must drop valueQuantity, otherwise the resource + // serialises two mutually-exclusive value* fields (invalid FHIR). + profile.setValueCodeableConcept({ text: "unable to obtain" }); + + expect(profile.getValueCodeableConcept()).toEqual({ text: "unable to obtain" }); + expect(profile.getValueQuantity()).toBeUndefined(); + + const resource = profile.toResource(); + const valueKeys = Object.keys(resource).filter((k) => k.startsWith("value")); + expect(valueKeys).toEqual(["valueCodeableConcept"]); + }); +}); From 57729624b9787cd2eee2a206dced9059fc766354 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 3 Jun 2026 10:22:00 +0200 Subject: [PATCH 3/3] ts: regenerate profile examples and snapshot for choice mutual exclusion --- .../Observation_observation_bodyweight.ts | 7 ++ .../profiles/Observation_observation_bp.ts | 7 ++ .../Observation_observation_vitalsigns.ts | 7 ++ .../Observation_observation_vitalsigns.ts | 7 ++ .../Observation_USCoreBloodPressureProfile.ts | 32 ++++++++ .../Observation_USCoreBodyWeightProfile.ts | 32 ++++++++ .../Observation_USCoreVitalSignsProfile.ts | 32 ++++++++ .../__snapshots__/typescript.test.ts.snap | 78 +++++++++++++++++++ 8 files changed, 202 insertions(+) diff --git a/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts b/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts index 5d5d6b51..1715c895 100644 --- a/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts +++ b/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts @@ -142,6 +142,7 @@ export class observation_bodyweightProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -151,6 +152,7 @@ export class observation_bodyweightProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -164,6 +166,11 @@ export class observation_bodyweightProfile { return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + // Extensions // Slices public setVSCat (input?: Observation_bodyweight_Category_VSCatSliceFlat | CodeableConcept): this { diff --git a/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts b/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts index a108c811..dc66a72c 100644 --- a/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts +++ b/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts @@ -177,6 +177,7 @@ export class observation_bpProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -186,6 +187,7 @@ export class observation_bpProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -199,6 +201,11 @@ export class observation_bpProfile { return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + // Extensions // Slices public setVSCat (input?: Observation_bp_Category_VSCatSliceFlat | CodeableConcept): this { diff --git a/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts b/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts index 2ddde60d..66f29771 100644 --- a/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts +++ b/examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts @@ -145,6 +145,7 @@ export class observation_vitalsignsProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -154,10 +155,16 @@ export class observation_vitalsignsProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + // Extensions // Slices public setVSCat (input?: Observation_vitalsigns_Category_VSCatSliceFlat | CodeableConcept): this { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts index 2ddde60d..66f29771 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts @@ -145,6 +145,7 @@ export class observation_vitalsignsProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -154,10 +155,16 @@ export class observation_vitalsignsProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + // Extensions // Slices public setVSCat (input?: Observation_vitalsigns_Category_VSCatSliceFlat | CodeableConcept): this { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts index 85129683..d46ab83f 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBloodPressureProfile.ts @@ -180,6 +180,7 @@ export class USCoreBloodPressureProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -189,6 +190,7 @@ export class USCoreBloodPressureProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -198,6 +200,7 @@ export class USCoreBloodPressureProfile { } setValueQuantity (value: Quantity) : this { + this.clearValue(); Object.assign(this.resource, { valueQuantity: value }); return this; } @@ -207,6 +210,7 @@ export class USCoreBloodPressureProfile { } setValueCodeableConcept (value: CodeableConcept) : this { + this.clearValue(); Object.assign(this.resource, { valueCodeableConcept: value }); return this; } @@ -216,6 +220,7 @@ export class USCoreBloodPressureProfile { } setValueString (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueString: value }); return this; } @@ -225,6 +230,7 @@ export class USCoreBloodPressureProfile { } setValueBoolean (value: boolean) : this { + this.clearValue(); Object.assign(this.resource, { valueBoolean: value }); return this; } @@ -234,6 +240,7 @@ export class USCoreBloodPressureProfile { } setValueInteger (value: number) : this { + this.clearValue(); Object.assign(this.resource, { valueInteger: value }); return this; } @@ -243,6 +250,7 @@ export class USCoreBloodPressureProfile { } setValueRange (value: Range) : this { + this.clearValue(); Object.assign(this.resource, { valueRange: value }); return this; } @@ -252,6 +260,7 @@ export class USCoreBloodPressureProfile { } setValueRatio (value: Ratio) : this { + this.clearValue(); Object.assign(this.resource, { valueRatio: value }); return this; } @@ -261,6 +270,7 @@ export class USCoreBloodPressureProfile { } setValueSampledData (value: SampledData) : this { + this.clearValue(); Object.assign(this.resource, { valueSampledData: value }); return this; } @@ -270,6 +280,7 @@ export class USCoreBloodPressureProfile { } setValueTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueTime: value }); return this; } @@ -279,6 +290,7 @@ export class USCoreBloodPressureProfile { } setValueDateTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueDateTime: value }); return this; } @@ -288,10 +300,30 @@ export class USCoreBloodPressureProfile { } setValuePeriod (value: Period) : this { + this.clearValue(); Object.assign(this.resource, { valuePeriod: value }); return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + + clearValue () : void { + delete this.resource.valueQuantity; + delete this.resource.valueCodeableConcept; + delete this.resource.valueString; + delete this.resource.valueBoolean; + delete this.resource.valueInteger; + delete this.resource.valueRange; + delete this.resource.valueRatio; + delete this.resource.valueSampledData; + delete this.resource.valueTime; + delete this.resource.valueDateTime; + delete this.resource.valuePeriod; + } + // Extensions // Slices public setVSCat (input?: USCoreBloodPressureProfile_Category_VSCatSliceFlat | CodeableConcept): this { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts index e2e0793c..6b51714b 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreBodyWeightProfile.ts @@ -145,6 +145,7 @@ export class USCoreBodyWeightProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -154,6 +155,7 @@ export class USCoreBodyWeightProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -163,6 +165,7 @@ export class USCoreBodyWeightProfile { } setValueQuantity (value: Quantity) : this { + this.clearValue(); Object.assign(this.resource, { valueQuantity: value }); return this; } @@ -172,6 +175,7 @@ export class USCoreBodyWeightProfile { } setValueCodeableConcept (value: CodeableConcept) : this { + this.clearValue(); Object.assign(this.resource, { valueCodeableConcept: value }); return this; } @@ -181,6 +185,7 @@ export class USCoreBodyWeightProfile { } setValueString (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueString: value }); return this; } @@ -190,6 +195,7 @@ export class USCoreBodyWeightProfile { } setValueBoolean (value: boolean) : this { + this.clearValue(); Object.assign(this.resource, { valueBoolean: value }); return this; } @@ -199,6 +205,7 @@ export class USCoreBodyWeightProfile { } setValueInteger (value: number) : this { + this.clearValue(); Object.assign(this.resource, { valueInteger: value }); return this; } @@ -208,6 +215,7 @@ export class USCoreBodyWeightProfile { } setValueRange (value: Range) : this { + this.clearValue(); Object.assign(this.resource, { valueRange: value }); return this; } @@ -217,6 +225,7 @@ export class USCoreBodyWeightProfile { } setValueRatio (value: Ratio) : this { + this.clearValue(); Object.assign(this.resource, { valueRatio: value }); return this; } @@ -226,6 +235,7 @@ export class USCoreBodyWeightProfile { } setValueSampledData (value: SampledData) : this { + this.clearValue(); Object.assign(this.resource, { valueSampledData: value }); return this; } @@ -235,6 +245,7 @@ export class USCoreBodyWeightProfile { } setValueTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueTime: value }); return this; } @@ -244,6 +255,7 @@ export class USCoreBodyWeightProfile { } setValueDateTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueDateTime: value }); return this; } @@ -253,10 +265,30 @@ export class USCoreBodyWeightProfile { } setValuePeriod (value: Period) : this { + this.clearValue(); Object.assign(this.resource, { valuePeriod: value }); return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + + clearValue () : void { + delete this.resource.valueQuantity; + delete this.resource.valueCodeableConcept; + delete this.resource.valueString; + delete this.resource.valueBoolean; + delete this.resource.valueInteger; + delete this.resource.valueRange; + delete this.resource.valueRatio; + delete this.resource.valueSampledData; + delete this.resource.valueTime; + delete this.resource.valueDateTime; + delete this.resource.valuePeriod; + } + // Extensions // Slices public setVSCat (input?: USCoreBodyWeightProfile_Category_VSCatSliceFlat | CodeableConcept): this { diff --git a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts index 0acbe045..b9b41eec 100644 --- a/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts +++ b/examples/typescript-us-core/fhir-types/hl7-fhir-us-core/profiles/Observation_USCoreVitalSignsProfile.ts @@ -149,6 +149,7 @@ export class USCoreVitalSignsProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -158,6 +159,7 @@ export class USCoreVitalSignsProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -167,6 +169,7 @@ export class USCoreVitalSignsProfile { } setValueQuantity (value: Quantity) : this { + this.clearValue(); Object.assign(this.resource, { valueQuantity: value }); return this; } @@ -176,6 +179,7 @@ export class USCoreVitalSignsProfile { } setValueCodeableConcept (value: CodeableConcept) : this { + this.clearValue(); Object.assign(this.resource, { valueCodeableConcept: value }); return this; } @@ -185,6 +189,7 @@ export class USCoreVitalSignsProfile { } setValueString (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueString: value }); return this; } @@ -194,6 +199,7 @@ export class USCoreVitalSignsProfile { } setValueBoolean (value: boolean) : this { + this.clearValue(); Object.assign(this.resource, { valueBoolean: value }); return this; } @@ -203,6 +209,7 @@ export class USCoreVitalSignsProfile { } setValueInteger (value: number) : this { + this.clearValue(); Object.assign(this.resource, { valueInteger: value }); return this; } @@ -212,6 +219,7 @@ export class USCoreVitalSignsProfile { } setValueRange (value: Range) : this { + this.clearValue(); Object.assign(this.resource, { valueRange: value }); return this; } @@ -221,6 +229,7 @@ export class USCoreVitalSignsProfile { } setValueRatio (value: Ratio) : this { + this.clearValue(); Object.assign(this.resource, { valueRatio: value }); return this; } @@ -230,6 +239,7 @@ export class USCoreVitalSignsProfile { } setValueSampledData (value: SampledData) : this { + this.clearValue(); Object.assign(this.resource, { valueSampledData: value }); return this; } @@ -239,6 +249,7 @@ export class USCoreVitalSignsProfile { } setValueTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueTime: value }); return this; } @@ -248,6 +259,7 @@ export class USCoreVitalSignsProfile { } setValueDateTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueDateTime: value }); return this; } @@ -257,10 +269,30 @@ export class USCoreVitalSignsProfile { } setValuePeriod (value: Period) : this { + this.clearValue(); Object.assign(this.resource, { valuePeriod: value }); return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + + clearValue () : void { + delete this.resource.valueQuantity; + delete this.resource.valueCodeableConcept; + delete this.resource.valueString; + delete this.resource.valueBoolean; + delete this.resource.valueInteger; + delete this.resource.valueRange; + delete this.resource.valueRatio; + delete this.resource.valueSampledData; + delete this.resource.valueTime; + delete this.resource.valueDateTime; + delete this.resource.valuePeriod; + } + // Extensions // Slices public setVSCat (input?: USCoreVitalSignsProfile_Category_VSCatSliceFlat | CodeableConcept): this { diff --git a/test/api/write-generator/__snapshots__/typescript.test.ts.snap b/test/api/write-generator/__snapshots__/typescript.test.ts.snap index f177e8fc..be239c4b 100644 --- a/test/api/write-generator/__snapshots__/typescript.test.ts.snap +++ b/test/api/write-generator/__snapshots__/typescript.test.ts.snap @@ -645,6 +645,7 @@ export class observation_bodyweightProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -654,6 +655,7 @@ export class observation_bodyweightProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -667,6 +669,11 @@ export class observation_bodyweightProfile { return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + // Extensions // Slices public setVSCat (input?: Observation_bodyweight_Category_VSCatSliceFlat | CodeableConcept): this { @@ -903,6 +910,7 @@ export class observation_bpProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -912,6 +920,7 @@ export class observation_bpProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -925,6 +934,11 @@ export class observation_bpProfile { return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + // Extensions // Slices public setVSCat (input?: Observation_bp_Category_VSCatSliceFlat | CodeableConcept): this { @@ -1477,6 +1491,7 @@ export class USCoreBloodPressureProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -1486,6 +1501,7 @@ export class USCoreBloodPressureProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -1495,6 +1511,7 @@ export class USCoreBloodPressureProfile { } setValueQuantity (value: Quantity) : this { + this.clearValue(); Object.assign(this.resource, { valueQuantity: value }); return this; } @@ -1504,6 +1521,7 @@ export class USCoreBloodPressureProfile { } setValueCodeableConcept (value: CodeableConcept) : this { + this.clearValue(); Object.assign(this.resource, { valueCodeableConcept: value }); return this; } @@ -1513,6 +1531,7 @@ export class USCoreBloodPressureProfile { } setValueString (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueString: value }); return this; } @@ -1522,6 +1541,7 @@ export class USCoreBloodPressureProfile { } setValueBoolean (value: boolean) : this { + this.clearValue(); Object.assign(this.resource, { valueBoolean: value }); return this; } @@ -1531,6 +1551,7 @@ export class USCoreBloodPressureProfile { } setValueInteger (value: number) : this { + this.clearValue(); Object.assign(this.resource, { valueInteger: value }); return this; } @@ -1540,6 +1561,7 @@ export class USCoreBloodPressureProfile { } setValueRange (value: Range) : this { + this.clearValue(); Object.assign(this.resource, { valueRange: value }); return this; } @@ -1549,6 +1571,7 @@ export class USCoreBloodPressureProfile { } setValueRatio (value: Ratio) : this { + this.clearValue(); Object.assign(this.resource, { valueRatio: value }); return this; } @@ -1558,6 +1581,7 @@ export class USCoreBloodPressureProfile { } setValueSampledData (value: SampledData) : this { + this.clearValue(); Object.assign(this.resource, { valueSampledData: value }); return this; } @@ -1567,6 +1591,7 @@ export class USCoreBloodPressureProfile { } setValueTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueTime: value }); return this; } @@ -1576,6 +1601,7 @@ export class USCoreBloodPressureProfile { } setValueDateTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueDateTime: value }); return this; } @@ -1585,10 +1611,30 @@ export class USCoreBloodPressureProfile { } setValuePeriod (value: Period) : this { + this.clearValue(); Object.assign(this.resource, { valuePeriod: value }); return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + + clearValue () : void { + delete this.resource.valueQuantity; + delete this.resource.valueCodeableConcept; + delete this.resource.valueString; + delete this.resource.valueBoolean; + delete this.resource.valueInteger; + delete this.resource.valueRange; + delete this.resource.valueRatio; + delete this.resource.valueSampledData; + delete this.resource.valueTime; + delete this.resource.valueDateTime; + delete this.resource.valuePeriod; + } + // Extensions // Slices public setVSCat (input?: USCoreBloodPressureProfile_Category_VSCatSliceFlat | CodeableConcept): this { @@ -1845,6 +1891,7 @@ export class USCoreBodyWeightProfile { } setEffectiveDateTime (value: string) : this { + this.clearEffective(); Object.assign(this.resource, { effectiveDateTime: value }); return this; } @@ -1854,6 +1901,7 @@ export class USCoreBodyWeightProfile { } setEffectivePeriod (value: Period) : this { + this.clearEffective(); Object.assign(this.resource, { effectivePeriod: value }); return this; } @@ -1863,6 +1911,7 @@ export class USCoreBodyWeightProfile { } setValueQuantity (value: Quantity) : this { + this.clearValue(); Object.assign(this.resource, { valueQuantity: value }); return this; } @@ -1872,6 +1921,7 @@ export class USCoreBodyWeightProfile { } setValueCodeableConcept (value: CodeableConcept) : this { + this.clearValue(); Object.assign(this.resource, { valueCodeableConcept: value }); return this; } @@ -1881,6 +1931,7 @@ export class USCoreBodyWeightProfile { } setValueString (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueString: value }); return this; } @@ -1890,6 +1941,7 @@ export class USCoreBodyWeightProfile { } setValueBoolean (value: boolean) : this { + this.clearValue(); Object.assign(this.resource, { valueBoolean: value }); return this; } @@ -1899,6 +1951,7 @@ export class USCoreBodyWeightProfile { } setValueInteger (value: number) : this { + this.clearValue(); Object.assign(this.resource, { valueInteger: value }); return this; } @@ -1908,6 +1961,7 @@ export class USCoreBodyWeightProfile { } setValueRange (value: Range) : this { + this.clearValue(); Object.assign(this.resource, { valueRange: value }); return this; } @@ -1917,6 +1971,7 @@ export class USCoreBodyWeightProfile { } setValueRatio (value: Ratio) : this { + this.clearValue(); Object.assign(this.resource, { valueRatio: value }); return this; } @@ -1926,6 +1981,7 @@ export class USCoreBodyWeightProfile { } setValueSampledData (value: SampledData) : this { + this.clearValue(); Object.assign(this.resource, { valueSampledData: value }); return this; } @@ -1935,6 +1991,7 @@ export class USCoreBodyWeightProfile { } setValueTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueTime: value }); return this; } @@ -1944,6 +2001,7 @@ export class USCoreBodyWeightProfile { } setValueDateTime (value: string) : this { + this.clearValue(); Object.assign(this.resource, { valueDateTime: value }); return this; } @@ -1953,10 +2011,30 @@ export class USCoreBodyWeightProfile { } setValuePeriod (value: Period) : this { + this.clearValue(); Object.assign(this.resource, { valuePeriod: value }); return this; } + clearEffective () : void { + delete this.resource.effectiveDateTime; + delete this.resource.effectivePeriod; + } + + clearValue () : void { + delete this.resource.valueQuantity; + delete this.resource.valueCodeableConcept; + delete this.resource.valueString; + delete this.resource.valueBoolean; + delete this.resource.valueInteger; + delete this.resource.valueRange; + delete this.resource.valueRatio; + delete this.resource.valueSampledData; + delete this.resource.valueTime; + delete this.resource.valueDateTime; + delete this.resource.valuePeriod; + } + // Extensions // Slices public setVSCat (input?: USCoreBodyWeightProfile_Category_VSCatSliceFlat | CodeableConcept): this {