Ahryman40k / typescript-fhir-types

Typescript / Javascript object model for FHIR standard
MIT License
126 stars 27 forks source link

How to check runtime type of an unknown resource #27

Closed mgramigna closed 3 years ago

mgramigna commented 3 years ago

Say I wanted to make a function that does a runtime check of the provided type, like so

// what is the type of desiredType?
function isValidFHIRType(desiredType: any, resource: any) {
  const checkResult = desiredType.decode(resource);
  return checkResult.isRight();
}

I'm not sure what the type of the desiredType argument would be. I saw that there is a RTTI_ResourceList exported by this library, but this is not a Typescript Type or Interface

Is it possible to do RTTI when the desired RTTI is unknown when calling the decode function?

Ahryman40k commented 3 years ago

Sorry for delay, I missed your message.

RTTI means RunTime Type Information. This is an object that describe how your object should be. With it, you can check if a resource has the desired shape.
It also means RTTI can't be unknown, you should always know what type you are expecting. for example, does my any object is an IObservation ?

RTTI_Observation.decode(**any**) // this does the job

if your any content json fits an observation, then your response should be 'right', 'left' otherwise.
If you are looking for interfaces, so you can use Ixxx ( IObservation, IPatient, ... ).
I guess you're issue is to know what kind of resource you are using. It's true that an helper method could be present is my library, specially for ResourceList type that merge all FHIR type. This is where typeguards can be useful. If your not familiar with this part of typescript, I let you learn more about it. Here is a small sample

// Here an interface definition 
interface IA {
  foo: 'IA';
  bar: string; 
}
// Here is a typeguard for IA
function isIA( json: any): json is IA {
  return json.foo === 'IA'
}
// Here is a an any definition. I forced any to prevent typescript to infer 'a' as { foo: string, bar: string }
const a: any = {
  foo: 'IA',
  bar: "A"
}
// Here another any type
const b: any = {
  foo: 'IB',
  other: "B"
}
// Let's test is a an IA
if ( isIA(a) ) {
 // You can see here that a is now type IA and IntelliSense works well with types.
  console.log( a.bar )
} else {
  console.log( 'a is not IA')
}
// Let's do the same with b
// b can't be considered as IA because foo is not of type 'IA' ( Like FHIR  'resourceType' property does )
if ( !isIA(b) ) {
  console.log( 'b is not an IA')
} else {
  console.log( 'b is IB')
}

Hope this helps. :)

mgramigna commented 3 years ago

Thanks for the response! Just to clarify, there is no current support in your library for doing RTTI when the FHIR resource type is unknown? i.e. you always need to call the specific interface (like your RTTI_Observation example)

Ahryman40k commented 3 years ago

the library is intended to validate a json as FHIR resource shape. Generally speaking, you know what that shape is intended to be because you are interrogating a service in purpose. If you ask for Foo you shouldn't receive Bar. There are specifications for service you use. The goal of the library is to enforce the typings. meaning if you receive something in json format, it may have a wrong shape because of a bug, typo, wrong spec, etc. Using a decode method allows you to safely assume an object is right at runtime.

if you really know nothing about your data, there is 2 ways:

function isPatient( data: any): data is IPatient { const result = RTTI_Patient.decode(data); return E.isRight(validationResult) !== undefined; // I didn't test the code. but this is the main idea. If you have a valid resource you have a valid type. }

const data: any = _your_datahere if( isPatient(data) ) { // typeguard // safely access patient here } else if( isObservation(data) ) { // typeguard // safely access Observation here } etc etc



You need typeguard for all resources. It's something I should add in future release.
mgramigna commented 3 years ago

Thank you. This answers my question