Validation of expressions given input sections

Is it possible to catch expressions that access (potentially) undefined variables before runtime ?

Take as an example the following diff as applied to https://github.com/common-workflow-library/bio-cwl-tools/blob/91c42fb809ce18eafe16155cca0abf362270c0fe/fastp/fastp.cwl:

diff --git a/fastp/fastp.cwl b/fastp/fastp.cwl
index 575c91f..b6be4eb 100755
--- a/fastp/fastp.cwl
+++ b/fastp/fastp.cwl
@@ -19,7 +19,7 @@ baseCommand: fastp

 arguments:
     - prefix: -o
-      valueFrom: $(inputs.fastq1.nameroot).fastp.fastq
+      valueFrom: $(inputs.fastq3.nameroot).fastp.fastq
     - |
       ${
         if (inputs.fastq2){

fastq3 here is not defined.

cwltool --validate --strict fastp.xml finds no issue with this. As I understand it, cwltool only checks for syntax errors via jslint, and anything nested under the inputs variable is not checked further.

It seems like it might be possible to generate a typescript interface for the input section of the document and to then generate a typescript document for any expression given the typed input object.

Has anyone experimented with this before ? I haven’t managed to produce a typescript interface for the inputs, even though it seems that this should theoretically be possible (even if we have minor type mismatches I believe this could be quite useful).

Hi @mvdbeek,

I think initially I misunderstood the assignment and wrote up the equivalent in TS code (shown here)

bio-cwl-tools/fastp/fastp.cwl at ts/fastp-poc · alexiswl/bio-cwl-tools (github.com)

I have noted however, this should be possible to auto-generate interfaces for TypeScript that emulate the cwl inputs and then wrap all the used JS expressions used in a CWL tool into functions that take in ‘inputs’ as a parameter where ‘inputs’ is of the type also defined by the autogenerated TS code? An example might help explain my rambling.

Part 1: Generate an inputs interface based on the inputs of the object. Note I’m using the rabix/cwlts library instead of cwl-ts-auto. I’ve explained a few differences here - TypeScript implementation makes FileTypes and DirectoryTypes difficult to work with · Issue #23 · common-workflow-lab/cwl-ts-auto (github.com)

// Author: Alexis Lucattini
// For assistance on generation of typescript expressions
// In CWL, please visit our wiki page at https://github.com/umccr/cwl-ica/wiki/TypeScript
// Imports
import {File as IFile} from "cwlts/mappings/v1.0/File"
import {Directory as IDirectory} from "cwlts/mappings/v1.0/Directory"

// Backward compatibility with --target es5
declare global {
    interface Set<T> {
    }

    interface Map<K, V> {
    }

    interface WeakSet<T> {
    }

    interface WeakMap<K extends object, V> {
    }
}

// Input types
export interface IFastpInputs {
    fastq1: IFile
    fastq2?: IFile
    threads: number
    qualified_phred_quality: number
    unqualified_phred_quality: number
    min_length_required: number
    force_polyg_tail_trimming?: boolean
    disable_trim_poly_g: boolean
    base_correction: boolean
}

Part 2 - wrap the JS snippets into functions where inputs is a single parameter. You could in theory have other CWL JS attributes in as parameters such as runtime.

// (Hypothetical) Auto-generated code when doing js validation
//       valueFrom: $(inputs.fastq1.nameroot).fastp.fastq
function evaluate_line_17_value_from_js_statement(inputs: IFastpInputs){
    return inputs.fastq1.nameroot
}

Then run tsc my-file.ts

Should return blank.

However, now let’s assume fastq1 in line 17 was accidentally references as fastq3 (as per your example above).

// (Hypothetical) Auto-generated code when doing js validation
//       valueFrom: $(inputs.fastq3.nameroot).fastp.fastq
function evaluate_line_17_value_from_js_statement(inputs: IFastpInputs){
    return inputs.fastq3.nameroot
}
tsc fastp-testing.ts

Instead now gives us:

fastp-testing.ts:43:19 - error TS2551: Property 'fastq3' does not exist on type 'IFastpInputs'. Did you mean 'fastq1'?

43     return inputs.fastq3.nameroot
                     ~~~~~~

  fastp-testing.ts:25:5
    25     fastq1: IFile
           ~~~~~~
    'fastq1' is declared here.


Found 1 error in fastp-testing.ts:43

Autogenerating TS from CWL shouldn’t be too difficult, particularly advances doen with the cwl-utils package. I use this to generate TypeScript interfaces for the various CWL schemas I create so I can then validate any JavaScript that interacts with the schemas (which I write that JavaScript in TypeScript now anyway - but use the Schema Interfaces for this purpose).

2 Likes