fhirpath.ts

TypeScript-native FHIRPath engine

Catch the mistake
before even testing.

A safer FHIRPath engine. Catch expression errors at build-time.
No tests that should be type checks. No surprises in production.
Type below and watch.

Expression

    
                
    Result

    What you just used

    The analyzer, spec §11

    Unknown elements, wrong types, singleton misuse, and equalities that can never hold — flagged statically. Runs in CI two ways: the fhirpath-check CLI, or the fhirpath/no-invalid-expressions ESLint rule.

    Types from plain tsc

    r4.evaluate('Patient.name.given.count() + 1', patient) is a number[]. The type-level parser applies FHIRPath precedence and function rules. No compiler plugin or app code generation is required.

    Exact by construction

    BigInt-scaled decimals mean 0.1 + 0.2 = 0.3 holds. Zero runtime dependencies, verified against the official HL7 R4 and R5 suites.

    Edit it — checked live, in your browser tsc types + analyzer · no server

    Loading the editor…

    Where a mistake gets caught

    TypeScript checks the values you pass in and the result types you get back. The fhirpath-check CLI / ESLint rule checks the expression itself against the FHIR model.

    As you type tsc · no plugin

    Type error

    const given = r4.compile('Patient.name.given')   // string[]
    
    given.evaluate(observation)             // won't compile: expects a Patient
    given.evaluate(patient)[0].toFixed(1)   // won't compile: it's a string

    A bounded type-level parser follows the runtime grammar, function rules, variables, Reference targets, and inferred host values. Explicit declarations refine ambiguous values. It computes safe TypeScript types but does not validate the expression; the analyzer below reports expression errors. Malformed, widened, over-budget, or deliberately opaque expressions become unknown[].

    Before you run fhirpath-check · ESLint · in CI

    Static analysis error

    analyzeExpression('Observation.valueQuantity', opts)
    // Element 'valueQuantity' is not defined on FHIR.Observation
    
    analyzeExpression('Patient.name.given.substring(1)', opts)
    // substring() expects a collection with a single item as input (spec §11)

    Typos, choice-key misuse, singleton misuse, equality that can never hold — the whole class TypeScript's type system can't express. The §11 analyzer reads the expression against the FHIR model and reports them without touching data. It ships two ways to run: fhirpath-check, a CLI that scans your .ts/.js files for FHIRPath literals (tags, compile(), evaluate()) and exits non-zero on any finding — drop it into a CI step; and fhirpath/no-invalid-expressions, an ESLint flat-config rule that flags the same literals inline in your editor and fails eslint. This is the layer the playground above runs live.

    When it runs default evaluator

    Lenient runtime result

    r4.evaluate('Patient.namee.given', patient)   // []  — no error, just empty

    evaluate() does not run static analysis by default. A path step that finds nothing returns [], so the typo in namee gives the same result as valid data that happens to be absent. The fhirpath-check CLI / ESLint rule above catches it before this line runs. Invalid function calls, unconvertible units, and cardinality violations still throw at runtime.