Stream: javascript
Topic: Playing with TypeScript export
Gino Canessa (Jan 14 2022 at 21:11):
Hi all, it's been a 'to-do' for a while to look at splitting out the TypeScript definitions to be have some runtime (e.g., classes), split into individual files, etc. I've started working on a pass of changes, using a new language definition to ensure I don't break anything for anyone else. The current R4 output is in a temp repo: https://github.com/GinoCanessa/FhirTypeScriptOddsAndEnds .
The code to generate it (for those playing with fhir-codegen) is in the dev branch.
There are still some things I'm not happy with (e.g., not sure about constructors vs. factory pattern to make parsing easier), but it's a start. Cheers!
Josh Mandel (Jan 14 2022 at 22:55):
If I'm constructing (say) a Condition from JSON, at what point do things like the CodeableConcept constructor for Condition.code get executed?
Josh Mandel (Jan 14 2022 at 22:57):
Do I need to invoke that myself, or will the Condition constructor eventually handle this, or is the expectation that CodeableConcept is acting only as an interferface and nobody ever needs to create instances of that class because a genetic object implementing the interface is sufficient? (If we start attaching behavior to the classes, that would force us into instantiating them...)
Brian Kaney (Jan 18 2022 at 22:16):
Cool! But I'm not sure I understand the use-case of these model classes.. Are we not able to enforce this validation with JSON schema at runtime (and rely on DT type definitions at develop time)?
In any case, I find my odds-and-ends usually ends up including a set of type predicate guards when I am dealing with polymorphic things, along these lines.:
export const isPatient = (resource: any): resource is fhir4.Patient => {
return resource.resourceType === 'Patient'
}
Josh Mandel (Jan 18 2022 at 22:25):
Are we not able to enforce this validation with JSON schema at runtime
Sure -- might want to incorporate something like https://ajv.js.org/ if the basis of runtime validation is JSON schema; embedding validation as a capability in classes work too though (and aligns with how some other FHIR language libraries work).
Gino Canessa (Jan 18 2022 at 22:34):
Thanks Brian! This is moving towards the concept of a 'fuller' JS/TS library, and is just one idea I had to move forward. I've looked at a few things for run-time type checking (like io-ts) but I want to see how much we can get without additional dependencies. Mostly because I've found people are opinionated about which one to use =).
I also found myself adding back things removed for DT type definitions during development.. Things like instances of enumerations for value-sets, which cannot live in interface files.
Gino Canessa (Jan 18 2022 at 22:37):
And blah, missed your earlier questions @Josh Mandel : That's the kind of thing I am testing with the definitions. I expect to make some pretty major changes to that pass - right now I'm trying to evaluate if it's better to move to a factory pattern so that you can choose a fromJson
and fromObject
separately in order to get different behaviors...
Basically, I just wanted to push and see what works and what doesn't.
Gino Canessa (Jan 18 2022 at 22:39):
re: ajv.js, I think I remember that the FHIR definitions did not play well with JSON Schema (some sort of design-assumption incompatibility). I'll add to my list to see if that's true or not though.
Josh Mandel (Jan 18 2022 at 23:01):
May be time to investigate https://datatracker.ietf.org/doc/html/rfc8927 for FHIR too! (Avj supports both -- FWIW we've used Avj with JSON schema for basic FHIR validation in SMART Health Cards tooling.)
John Moehrke (Jan 18 2022 at 23:43):
the json community re-inventing XML... how adorable
Josh Mandel (Jan 19 2022 at 00:47):
Show me the namespaces!
Mike Lohmeier (Jan 25 2022 at 16:05):
We tried using the JSON schemas provided in the r4 downloads, https://www.hl7.org/fhir/fhir.schema.json.zip, before and hit a slew of semantic validations not raising as errors. For example, required fields are not being marked as required. We ended up falling back to the HAPI java validator because so much was missing from the downloadable JSON schemas. I don't think this is a problem with the js JSON validator libs but the actual schemas being generated for the downloads in fhir r4.
Just an FYI for anyone fully depending on those FHIR JSON schemas only.
Gino Canessa (Jan 28 2022 at 22:06):
Ok, so the next pass is posted for thoughts. Right now, I've added another tree of exports for interfaces. So, the root index exports interfaces
, models
, and valuesets
. There's been a bit of back and forth about required elements/constructors, so what I have tentatively landed on is:
- Define the interface that contains the elements for a type.
- Optional elements are decorated with
?
syntax, but all elements have|undefined
- this allows for adding required properties later, but still have good hinting on what is actually required. - Classes implement the relevant interface (e.g.,
fhirModels.Patient implements fhirInterfaces.IPatient
). - Constructors use the
Partial<T>
hint so that you can create a class without all the required elements. - Classes define a static factory function (
CreateStrict
right now) that takes the interface without a partial tag, so that you can ensure you have all required elements. It throws if something is missing. - Classes define an instance function (
checkRequiredElements
right now) that checks to see if the instance has values for all required elements (incomplete, but should be enough to give the idea... I have not looked at choice types yet).
I have been focused on the generation, so I have not used this output yet (I did make sure it compiles =). I will get to that when I can, but thought it would be good to post before the weekend.
Cheers!
(edit: one thing I know I want to update is adding an interface around those functions so that they can be used generically).
(edit2: Also, there is a FhirResourceFactory
function in models.ts
which uses the resourceType
value to create a resource of that type)
(edit3: Files are on GitHub =)
Gino Canessa (Jan 28 2022 at 22:55):
Another note: something I want to evaluate is either using a model like this, or using a separate set of 'strict' models and 'optional' models that both get exported.
Gino Canessa (Jan 31 2022 at 20:24):
Ok, pushed another pass up to GitHub. I was not happy with how convoluted everything got on the last pass, so I split everything into a 'strict' and 'optional' version.
All interfaces and models are just replicated between the two, so that you can pick and choose when importing:
import { OptionalModels as fhir4 } from './index';
import { Patient, OperationOutcome } from './strictmodels';
The CreateStrict
and checkRequiredElements
still exist on the optional, but not on the strict versions. I would like to add some sort of opposite function to strict (e.g., check if some arbitrary object has the elements to be of a class), but have not added it yet.
Any comments/thoughts/feedback welcome.
Last updated: Apr 12 2022 at 19:14 UTC