Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,47 @@ assets/api/writer-generator/
- Use assets for static runtime code shared across all generated profiles (helpers, validators, base models)
- Use programmatic generation (`w.lineSM()`, `w.curlyBlock()`) for code that varies per schema/profile

## TypeScript Profile API

### Slice Types

Each non-type-discriminated slice generates two types:
- **`SliceFlat`** — setter input, discriminator fields omitted (auto-applied by setter)
- **`SliceFlatAll`** — getter return, extends `SliceFlat` with readonly discriminator literals

```typescript
// Setter input — only user data
export type VSCatSliceFlat = Omit<CodeableConcept, "coding">;
// Getter return — includes discriminator values
export type VSCatSliceFlatAll = VSCatSliceFlat & {
readonly coding: [{ code: "vital-signs"; system: "http://...observation-category" }];
}
```

Type-discriminated slices (e.g. `BundleEntry<Patient>`) use the typed base type directly — no `SliceFlat`/`SliceFlatAll` generated.

### Unbounded Slices (max: *)

Slices with `max: *` use array-based API:
- **Setter**: `setOrganizationEntry(entries[])` — replaces all matched elements
- **Getter**: `getOrganizationEntry()` — returns `T[] | undefined`

Single-element slices (`max: 1`) keep the existing single-item API.

### Reference Types for Family Types

When a reference target is a family type (e.g. `Resource`, `DomainResource`), the generated type uses `Reference<string /* Resource */>` instead of `Reference<"Resource">`. This makes narrower profile references like `Reference<"Patient">` assignable to the base type field.

Detection uses `mkIsFamilyType(tsIndex)` which checks `schema.typeFamily.resources.length > 0`.

### Slice Field Validation

`validate()` checks required fields inside matched slice elements via `validateSliceFields`. For constrained choice slices (e.g. BP `component.value[x]` restricted to `valueQuantity`), the variant is validated as required:

```
"observation-bp.component[SystolicBP].valueQuantity is required"
```

## Common Development Patterns

### Adding a New Generator Feature
Expand Down
13 changes: 0 additions & 13 deletions assets/api/writer-generator/typescript/profile-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,19 +203,6 @@ export const extractComplexExtension = <T = Record<string, unknown>>(
// Slice helpers
// ---------------------------------------------------------------------------

/**
* Remove discriminator keys from a slice element, returning only the
* user-supplied portion. Used by slice getters so callers see a clean object
* without the fixed discriminator values baked in.
*/
export const stripMatchKeys = <T>(slice: object, matchKeys: string[]): T => {
const result = { ...slice } as Record<string, unknown>;
for (const key of matchKeys) {
delete result[key];
}
return result as T;
};

/**
* Wrap a flat input object under a choice-type key before inserting into a
* slice. For example, a Quantity value destined for `valueQuantity` is
Expand Down
13 changes: 0 additions & 13 deletions examples/typescript-r4/fhir-types/profile-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,19 +203,6 @@ export const extractComplexExtension = <T = Record<string, unknown>>(
// Slice helpers
// ---------------------------------------------------------------------------

/**
* Remove discriminator keys from a slice element, returning only the
* user-supplied portion. Used by slice getters so callers see a clean object
* without the fixed discriminator values baked in.
*/
export const stripMatchKeys = <T>(slice: object, matchKeys: string[]): T => {
const result = { ...slice } as Record<string, unknown>;
for (const key of matchKeys) {
delete result[key];
}
return result as T;
};

/**
* Wrap a flat input object under a choice-type key before inserting into a
* slice. For example, a Quantity value destined for `valueQuantity` is
Expand Down
13 changes: 0 additions & 13 deletions examples/typescript-us-core/fhir-types/profile-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,19 +203,6 @@ export const extractComplexExtension = <T = Record<string, unknown>>(
// Slice helpers
// ---------------------------------------------------------------------------

/**
* Remove discriminator keys from a slice element, returning only the
* user-supplied portion. Used by slice getters so callers see a clean object
* without the fixed discriminator values baked in.
*/
export const stripMatchKeys = <T>(slice: object, matchKeys: string[]): T => {
const result = { ...slice } as Record<string, unknown>;
for (const key of matchKeys) {
delete result[key];
}
return result as T;
};

/**
* Wrap a flat input object under a choice-type key before inserting into a
* slice. For example, a Quantity value destined for `valueQuantity` is
Expand Down
Loading