Ahryman40k / typescript-fhir-types

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

Reject additional properties #23

Closed TylerHaigh closed 3 years ago

TylerHaigh commented 3 years ago

Hi, Is it possible for the library to reject additional properties not defined in the FHIR Schema? For example, the following object is accepted by the decode api

{
    "resourceType": "Location",
    "AdditionalField": "IsNotAllowed"
}
const validationResult = R4.RTTI_Location.decode(obj);

    if (validationResult.isLeft()) {
      return {
        status: 400,
        body: validationResult
      }
    }

    return {
      status: 200,
      body: 'valid'
    }
Ahryman40k commented 3 years ago

Hi,

I think you talk about a io-ts functionality:

Exact types

You can make a codec exact (which means that additional properties are stripped) using the exact combinator

see here

The fact is FHIR is a free definition, so you can define your own resources and profiles. Meaning I choose not to check object exact. You can create a complete override to make io-ts object definition exact.

Hope it helps. regards

TylerHaigh commented 3 years ago

Thanks @Ahryman40k We'll check it out

oatcoder commented 3 years ago

@TylerHaigh do you have an example of you exact combinator?

@Ahryman40k how can I use RTTI_Person on io-ts 'exact'?

Ahryman40k commented 3 years ago

If you look at the RTTI_Person code. You'll see something like this

export const RTTI_Person: t.Type<IPerson> = t.recursion('IPerson', () =>
    t.intersection([
        t.type({
            resourceType: t.literal('Person')
        }),
        t.partial({
            id: RTTI_id,
            meta: RTTI_Meta,
            implicitRules: RTTI_uri,
            _implicitRules: RTTI_Element,
            language: RTTI_code,
            _language: RTTI_Element,
            text: RTTI_Narrative,
            contained: t.array(RTTI_ResourceList),
            extension: t.array(RTTI_Extension),
            modifierExtension: t.array(RTTI_Extension),
            identifier: t.array(RTTI_Identifier),
            name: t.array(RTTI_HumanName),
            telecom: t.array(RTTI_ContactPoint),
            gender: createEnumType<PersonGenderKind>(
                PersonGenderKind,
                'PersonGenderKind'
            ),
            _gender: RTTI_Element,
            birthDate: RTTI_date,
            _birthDate: RTTI_Element,
            address: t.array(RTTI_Address),
            photo: RTTI_Attachment,
            managingOrganization: RTTI_Reference,
            active: t.boolean,
            _active: RTTI_Element,
            link: t.array(RTTI_Person_Link)
        })
    ])
);

I use io-ts to compose a type that describe what properties should be found in my object.
It is composed of mandatory and optional properties.

When you decode a json, it means you can 'test' if you json respect the rules. Your json can also contains other properties. Actually nothing prevents you to extend an object type (inherited from interface IPerson) If you want to test against an exact definition and prevent additional properties to exist then you have to add an additional rule on the io-ts definition: t.exact. (see here). You need to override my own io-ts objects definition

       const exact_person = t.exact(RTTI_Person)
       ...
       // then
       exact_person.decode(**json**) // will now be Left if json contains other properties than expected 

Hope it helps.