Skip to content

Commit 5da8a8d

Browse files
authored
Merge pull request #127 from atomic-ehr/fix-slice-flat-discriminator-types
TS: Split slice flat types into SliceFlat/SliceFlatAll with discriminator literals
2 parents 57eb30c + f7a41b8 commit 5da8a8d

18 files changed

Lines changed: 390 additions & 204 deletions

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import type { Quantity } from "../../hl7-fhir-r4-core/Quantity";
99
import type { Reference } from "../../hl7-fhir-r4-core/Reference";
1010

1111
export type Observation_bodyweight_Category_VSCatSliceFlat = Omit<CodeableConcept, "coding">;
12+
export type Observation_bodyweight_Category_VSCatSliceFlatAll = Observation_bodyweight_Category_VSCatSliceFlat & {
13+
readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }];
14+
}
1215

1316
import {
1417
ensureProfile,
@@ -17,7 +20,6 @@ import {
1720
setArraySlice,
1821
getArraySlice,
1922
ensureSliceDefaults,
20-
stripMatchKeys,
2123
validateRequired,
2224
validateExcluded,
2325
validateFixedValue,
@@ -38,7 +40,9 @@ export type observation_bodyweightProfileRaw = {
3840
export class observation_bodyweightProfile {
3941
static readonly canonicalUrl = "http://hl7.org/fhir/StructureDefinition/bodyweight";
4042

41-
private static readonly VSCatSliceMatch: Record<string, unknown> = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]};
43+
private static readonly VSCatSliceMatch: Record<string, unknown> = {
44+
"coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}],
45+
}
4246

4347
private resource: Observation;
4448

@@ -166,15 +170,15 @@ export class observation_bodyweightProfile {
166170
return this
167171
}
168172

169-
public getVSCat(mode: 'flat'): Observation_bodyweight_Category_VSCatSliceFlat | undefined;
173+
public getVSCat(mode: 'flat'): Observation_bodyweight_Category_VSCatSliceFlatAll | undefined;
170174
public getVSCat(mode: 'raw'): CodeableConcept | undefined;
171-
public getVSCat(): Observation_bodyweight_Category_VSCatSliceFlat | undefined;
172-
public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bodyweight_Category_VSCatSliceFlat | CodeableConcept | undefined {
175+
public getVSCat(): Observation_bodyweight_Category_VSCatSliceFlatAll | undefined;
176+
public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bodyweight_Category_VSCatSliceFlatAll | CodeableConcept | undefined {
173177
const match = observation_bodyweightProfile.VSCatSliceMatch
174178
const item = getArraySlice(this.resource.category, match)
175179
if (!item) return undefined
176180
if (mode === 'raw') return item
177-
return stripMatchKeys<Observation_bodyweight_Category_VSCatSliceFlat>(item, ["coding"])
181+
return item as unknown as Observation_bodyweight_Category_VSCatSliceFlatAll
178182
}
179183

180184
// Validation

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bp.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@ import type { Quantity } from "../../hl7-fhir-r4-core/Quantity";
99
import type { Reference } from "../../hl7-fhir-r4-core/Reference";
1010

1111
export type Observation_bp_Category_VSCatSliceFlat = Omit<CodeableConcept, "coding">;
12+
export type Observation_bp_Category_VSCatSliceFlatAll = Observation_bp_Category_VSCatSliceFlat & {
13+
readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }];
14+
}
15+
1216
export type Observation_bp_Component_SystolicBPSliceFlat = Omit<ObservationComponent, "code" | "value" | "valueQuantity" | "valueCodeableConcept" | "valueString" | "valueBoolean" | "valueInteger" | "valueRange" | "valueRatio" | "valueSampledData" | "valueTime" | "valueDateTime" | "valuePeriod"> & Quantity;
17+
export type Observation_bp_Component_SystolicBPSliceFlatAll = Observation_bp_Component_SystolicBPSliceFlat;
18+
1319
export type Observation_bp_Component_DiastolicBPSliceFlat = Omit<ObservationComponent, "code" | "value" | "valueQuantity" | "valueCodeableConcept" | "valueString" | "valueBoolean" | "valueInteger" | "valueRange" | "valueRatio" | "valueSampledData" | "valueTime" | "valueDateTime" | "valuePeriod"> & Quantity;
20+
export type Observation_bp_Component_DiastolicBPSliceFlatAll = Observation_bp_Component_DiastolicBPSliceFlat;
1421

1522
import {
1623
ensureProfile,
@@ -19,7 +26,6 @@ import {
1926
setArraySlice,
2027
getArraySlice,
2128
ensureSliceDefaults,
22-
stripMatchKeys,
2329
wrapSliceChoice,
2430
unwrapSliceChoice,
2531
validateRequired,
@@ -43,9 +49,15 @@ export type observation_bpProfileRaw = {
4349
export class observation_bpProfile {
4450
static readonly canonicalUrl = "http://hl7.org/fhir/StructureDefinition/bp";
4551

46-
private static readonly VSCatSliceMatch: Record<string, unknown> = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]};
47-
private static readonly SystolicBPSliceMatch: Record<string, unknown> = {"code":{"coding":[{"code":"8480-6","system":"http://loinc.org"}]}};
48-
private static readonly DiastolicBPSliceMatch: Record<string, unknown> = {"code":{"coding":[{"code":"8462-4","system":"http://loinc.org"}]}};
52+
private static readonly VSCatSliceMatch: Record<string, unknown> = {
53+
"coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}],
54+
}
55+
private static readonly SystolicBPSliceMatch: Record<string, unknown> = {
56+
"code": {"coding":[{"code":"8480-6","system":"http://loinc.org"}]},
57+
}
58+
private static readonly DiastolicBPSliceMatch: Record<string, unknown> = {
59+
"code": {"coding":[{"code":"8462-4","system":"http://loinc.org"}]},
60+
}
4961

5062
private resource: Observation;
5163

@@ -217,37 +229,37 @@ export class observation_bpProfile {
217229
return this
218230
}
219231

220-
public getVSCat(mode: 'flat'): Observation_bp_Category_VSCatSliceFlat | undefined;
232+
public getVSCat(mode: 'flat'): Observation_bp_Category_VSCatSliceFlatAll | undefined;
221233
public getVSCat(mode: 'raw'): CodeableConcept | undefined;
222-
public getVSCat(): Observation_bp_Category_VSCatSliceFlat | undefined;
223-
public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Category_VSCatSliceFlat | CodeableConcept | undefined {
234+
public getVSCat(): Observation_bp_Category_VSCatSliceFlatAll | undefined;
235+
public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Category_VSCatSliceFlatAll | CodeableConcept | undefined {
224236
const match = observation_bpProfile.VSCatSliceMatch
225237
const item = getArraySlice(this.resource.category, match)
226238
if (!item) return undefined
227239
if (mode === 'raw') return item
228-
return stripMatchKeys<Observation_bp_Category_VSCatSliceFlat>(item, ["coding"])
240+
return item as unknown as Observation_bp_Category_VSCatSliceFlatAll
229241
}
230242

231-
public getSystolicBP(mode: 'flat'): Observation_bp_Component_SystolicBPSliceFlat | undefined;
243+
public getSystolicBP(mode: 'flat'): Observation_bp_Component_SystolicBPSliceFlatAll | undefined;
232244
public getSystolicBP(mode: 'raw'): ObservationComponent | undefined;
233-
public getSystolicBP(): Observation_bp_Component_SystolicBPSliceFlat | undefined;
234-
public getSystolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_SystolicBPSliceFlat | ObservationComponent | undefined {
245+
public getSystolicBP(): Observation_bp_Component_SystolicBPSliceFlatAll | undefined;
246+
public getSystolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_SystolicBPSliceFlatAll | ObservationComponent | undefined {
235247
const match = observation_bpProfile.SystolicBPSliceMatch
236248
const item = getArraySlice(this.resource.component, match)
237249
if (!item) return undefined
238250
if (mode === 'raw') return item
239-
return unwrapSliceChoice<Observation_bp_Component_SystolicBPSliceFlat>(item, ["code"], "valueQuantity")
251+
return unwrapSliceChoice<Observation_bp_Component_SystolicBPSliceFlatAll>(item, ["code"], "valueQuantity")
240252
}
241253

242-
public getDiastolicBP(mode: 'flat'): Observation_bp_Component_DiastolicBPSliceFlat | undefined;
254+
public getDiastolicBP(mode: 'flat'): Observation_bp_Component_DiastolicBPSliceFlatAll | undefined;
243255
public getDiastolicBP(mode: 'raw'): ObservationComponent | undefined;
244-
public getDiastolicBP(): Observation_bp_Component_DiastolicBPSliceFlat | undefined;
245-
public getDiastolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_DiastolicBPSliceFlat | ObservationComponent | undefined {
256+
public getDiastolicBP(): Observation_bp_Component_DiastolicBPSliceFlatAll | undefined;
257+
public getDiastolicBP (mode: 'flat' | 'raw' = 'flat'): Observation_bp_Component_DiastolicBPSliceFlatAll | ObservationComponent | undefined {
246258
const match = observation_bpProfile.DiastolicBPSliceMatch
247259
const item = getArraySlice(this.resource.component, match)
248260
if (!item) return undefined
249261
if (mode === 'raw') return item
250-
return unwrapSliceChoice<Observation_bp_Component_DiastolicBPSliceFlat>(item, ["code"], "valueQuantity")
262+
return unwrapSliceChoice<Observation_bp_Component_DiastolicBPSliceFlatAll>(item, ["code"], "valueQuantity")
251263
}
252264

253265
// Validation

examples/typescript-r4/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import type { Period } from "../../hl7-fhir-r4-core/Period";
88
import type { Reference } from "../../hl7-fhir-r4-core/Reference";
99

1010
export type Observation_vitalsigns_Category_VSCatSliceFlat = Omit<CodeableConcept, "coding">;
11+
export type Observation_vitalsigns_Category_VSCatSliceFlatAll = Observation_vitalsigns_Category_VSCatSliceFlat & {
12+
readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }];
13+
}
1114

1215
import {
1316
ensureProfile,
@@ -16,7 +19,6 @@ import {
1619
setArraySlice,
1720
getArraySlice,
1821
ensureSliceDefaults,
19-
stripMatchKeys,
2022
validateRequired,
2123
validateExcluded,
2224
validateFixedValue,
@@ -38,7 +40,9 @@ export type observation_vitalsignsProfileRaw = {
3840
export class observation_vitalsignsProfile {
3941
static readonly canonicalUrl = "http://hl7.org/fhir/StructureDefinition/vitalsigns";
4042

41-
private static readonly VSCatSliceMatch: Record<string, unknown> = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]};
43+
private static readonly VSCatSliceMatch: Record<string, unknown> = {
44+
"coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}],
45+
}
4246

4347
private resource: Observation;
4448

@@ -159,15 +163,15 @@ export class observation_vitalsignsProfile {
159163
return this
160164
}
161165

162-
public getVSCat(mode: 'flat'): Observation_vitalsigns_Category_VSCatSliceFlat | undefined;
166+
public getVSCat(mode: 'flat'): Observation_vitalsigns_Category_VSCatSliceFlatAll | undefined;
163167
public getVSCat(mode: 'raw'): CodeableConcept | undefined;
164-
public getVSCat(): Observation_vitalsigns_Category_VSCatSliceFlat | undefined;
165-
public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_vitalsigns_Category_VSCatSliceFlat | CodeableConcept | undefined {
168+
public getVSCat(): Observation_vitalsigns_Category_VSCatSliceFlatAll | undefined;
169+
public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_vitalsigns_Category_VSCatSliceFlatAll | CodeableConcept | undefined {
166170
const match = observation_vitalsignsProfile.VSCatSliceMatch
167171
const item = getArraySlice(this.resource.category, match)
168172
if (!item) return undefined
169173
if (mode === 'raw') return item
170-
return stripMatchKeys<Observation_vitalsigns_Category_VSCatSliceFlat>(item, ["coding"])
174+
return item as unknown as Observation_vitalsigns_Category_VSCatSliceFlatAll
171175
}
172176

173177
// Validation

examples/typescript-r4/profile-bodyweight.test.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
import { describe, expect, test } from "bun:test";
66
import type { Observation } from "./fhir-types/hl7-fhir-r4-core/Observation";
7-
import { observation_bodyweightProfile as bodyweightProfile } from "./fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight";
7+
import {
8+
observation_bodyweightProfile as bodyweightProfile,
9+
type Observation_bodyweight_Category_VSCatSliceFlat as VSCatFlat,
10+
type Observation_bodyweight_Category_VSCatSliceFlatAll as VSCatFlatAll,
11+
} from "./fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_bodyweight";
812

913
describe("demo: create a bodyweight observation", () => {
1014
test("build a valid bodyweight resource step by step", () => {
@@ -95,13 +99,12 @@ describe("demo: read a bodyweight observation from JSON", () => {
9599
// Code is a fixed value — auto-set by the profile, read-only (no setter)
96100
expect(profile.getCode()!.coding![0]!.code).toBe("29463-7");
97101

98-
// Slice accessor strips discriminator keys (coding) by default — only user data remains
99-
expect(profile.getVSCat()).toEqual({ text: "Vital Signs" });
100-
// Use "raw" mode to see the full element including discriminator
101-
expect(profile.getVSCat("raw")!.coding).toEqual([
102-
{ code: "vital-signs", system: "http://terminology.hl7.org/CodeSystem/observation-category" },
103-
]);
104-
expect(profile.getVSCat("raw")!.text).toBe("Vital Signs");
102+
// Slice getter returns SliceFlat — includes both user data and discriminator values
103+
const vsCat = profile.getVSCat()!;
104+
expect(vsCat).toEqual({
105+
text: "Vital Signs",
106+
coding: [{ code: "vital-signs", system: "http://terminology.hl7.org/CodeSystem/observation-category" }],
107+
});
105108
});
106109
});
107110

@@ -123,13 +126,19 @@ describe("factory method equivalence", () => {
123126
});
124127

125128
describe("slice accessors", () => {
126-
test("setVSCat merges discriminator and strips it in flat mode", () => {
129+
test("setVSCat accepts SliceFlatInput, getVSCat returns SliceFlat", () => {
127130
const profile = bodyweightProfile.create({ status: "final", subject: { reference: "Patient/pt-1" } });
128-
profile.setVSCat({ text: "Vital Signs" });
129131

130-
expect(profile.getVSCat()).toEqual({ text: "Vital Signs" });
131-
expect(profile.getVSCat("raw")!.coding).toBeDefined();
132-
expect(profile.getVSCat("raw")!.text).toBe("Vital Signs");
132+
// Setter accepts SliceFlatInput — only user-supplied fields, no discriminators
133+
const input: VSCatFlat = { text: "Vital Signs" };
134+
profile.setVSCat(input);
135+
136+
// Getter returns SliceFlatAll — includes discriminator values + user data
137+
const flat: VSCatFlatAll = profile.getVSCat()!;
138+
expect(flat).toEqual({
139+
text: "Vital Signs",
140+
coding: [{ code: "vital-signs", system: "http://terminology.hl7.org/CodeSystem/observation-category" }],
141+
});
133142
});
134143

135144
test("setVSCat replaces existing slice element", () => {

examples/typescript-us-core/fhir-types/hl7-fhir-r4-core/profiles/Observation_observation_vitalsigns.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import type { Period } from "../../hl7-fhir-r4-core/Period";
88
import type { Reference } from "../../hl7-fhir-r4-core/Reference";
99

1010
export type Observation_vitalsigns_Category_VSCatSliceFlat = Omit<CodeableConcept, "coding">;
11+
export type Observation_vitalsigns_Category_VSCatSliceFlatAll = Observation_vitalsigns_Category_VSCatSliceFlat & {
12+
readonly coding: [{ code: "vital-signs"; system: "http://terminology.hl7.org/CodeSystem/observation-category" }];
13+
}
1114

1215
import {
1316
ensureProfile,
@@ -16,7 +19,6 @@ import {
1619
setArraySlice,
1720
getArraySlice,
1821
ensureSliceDefaults,
19-
stripMatchKeys,
2022
validateRequired,
2123
validateExcluded,
2224
validateFixedValue,
@@ -38,7 +40,9 @@ export type observation_vitalsignsProfileRaw = {
3840
export class observation_vitalsignsProfile {
3941
static readonly canonicalUrl = "http://hl7.org/fhir/StructureDefinition/vitalsigns";
4042

41-
private static readonly VSCatSliceMatch: Record<string, unknown> = {"coding":[{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}]};
43+
private static readonly VSCatSliceMatch: Record<string, unknown> = {
44+
"coding": [{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}],
45+
}
4246

4347
private resource: Observation;
4448

@@ -159,15 +163,15 @@ export class observation_vitalsignsProfile {
159163
return this
160164
}
161165

162-
public getVSCat(mode: 'flat'): Observation_vitalsigns_Category_VSCatSliceFlat | undefined;
166+
public getVSCat(mode: 'flat'): Observation_vitalsigns_Category_VSCatSliceFlatAll | undefined;
163167
public getVSCat(mode: 'raw'): CodeableConcept | undefined;
164-
public getVSCat(): Observation_vitalsigns_Category_VSCatSliceFlat | undefined;
165-
public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_vitalsigns_Category_VSCatSliceFlat | CodeableConcept | undefined {
168+
public getVSCat(): Observation_vitalsigns_Category_VSCatSliceFlatAll | undefined;
169+
public getVSCat (mode: 'flat' | 'raw' = 'flat'): Observation_vitalsigns_Category_VSCatSliceFlatAll | CodeableConcept | undefined {
166170
const match = observation_vitalsignsProfile.VSCatSliceMatch
167171
const item = getArraySlice(this.resource.category, match)
168172
if (!item) return undefined
169173
if (mode === 'raw') return item
170-
return stripMatchKeys<Observation_vitalsigns_Category_VSCatSliceFlat>(item, ["coding"])
174+
return item as unknown as Observation_vitalsigns_Category_VSCatSliceFlatAll
171175
}
172176

173177
// Validation

0 commit comments

Comments
 (0)