I maintained a shared DataWeave utility library across 12 Mule apps for 2 years. Functions like topN, pipe, and safeGet. All untyped. All accepting Any. All silently producing wrong output when called with wrong types.
Last quarter I rewrote them with DataWeave 2.5 call-site generics. Three production bugs surfaced on the first compile.
TL;DR
- DataWeave 2.5 adds Java/TypeScript-style call-site type parameters:
fun topN<T>(...) - The compiler validates T at every call site — type mismatches become compile errors, not runtime surprises
- Requires Mule 4.5+ — older runtimes throw parse errors on
<T>syntax - I caught 3 bugs that had been producing wrong output for 4 months
The Problem: Untyped Utility Functions
My library had functions like this:
fun topN(items, n, comp) = (items orderBy -comp($))[0 to (n - 1)]
Takes anything. Returns anything. Works on Arrays of Numbers, Strings, Objects. But there's no type checking at the call site.
If someone calls topN(arrayOfStrings, 3, (s) -> s) expecting numeric sorting, they get alphabetical sorting. No error. No warning. Wrong data.
The Fix: Call-Site Generics
DataWeave 2.5 introduced type parameters at the call site:
fun topN<T>(items: Array<T>, n: Number, comp: (T) -> Comparable): Array<T> =
(items orderBy -comp($))[0 to (n - 1)]
Now calling it:
topN<Number>(payload.numbers, 3, (n) -> n)
The compiler checks that payload.numbers is Array<Number>. If it's Array<String>, you get a compile error. Not a runtime surprise 4 months later.
100 production-ready DataWeave patterns with tests: mulesoft-cookbook on GitHub
The pipe Function: Type-Safe Composition
The pipe function chains transformations:
fun pipe<T>(value: T, fns: Array<(T) -> T>): T =
fns reduce (fn, acc = value) -> fn(acc)
Usage:
pipe<Array<Number>>(payload.numbers, [
(arr) -> arr distinctBy $,
(arr) -> arr orderBy $
])
Every function in the array must accept and return Array<Number>. If one function returns a different shape, the compiler catches it.
Before generics, a function that returned Array<Object> instead of Array<Number> would silently change the data shape downstream. With <T> enforced, every step in the chain must maintain the type contract.
The 3 Bugs Generics Caught
Bug 1: Wrong sort order for 4 months. A flow passed Array<Object> (customer records) to topN expecting Array<Number>. The comparator extracted a String field instead of Number. Sorting was alphabetical, not by magnitude. "Customer #9" ranked above "Customer #10" because "9" > "1" in string comparison.
Bug 2: Shape mismatch in pipe chain. Step 1 in a pipe chain returned Array<Object> but step 2 expected Array<Number>. The output was nested objects where flat numbers were expected. Downstream calculations used the nested objects as numbers — JavaScript-style coercion produced non-obvious wrong values.
Bug 3: Comparator type confusion. A topN call used (r) -> r.score where r.score was a String "95" not Number 95. The ordering was lexicographic: "87" > "100" because "8" > "1". Top 3 by score returned the wrong 3 records.
The Trap: Runtime Version Incompatibility
This requires DataWeave 2.5 (Mule 4.5+). The <T> syntax doesn't parse on older runtimes.
If your team runs mixed Mule versions — some apps on 4.5, some on 4.4 — a shared module with generics compiles on one server and crashes on another.
I version-gate my modules now:
-
modules/utils-v25.dwl— generics, for Mule 4.5+ apps -
modules/utils.dwl— no generics, backward compatible
The 4.4 apps use the untyped version until they're upgraded. Not ideal, but better than breaking production.
When to Use Generics
| Use generics when | Don't bother when |
|---|---|
| Shared utility functions used across 3+ apps | One-off inline transforms |
| Functions that accept multiple types (topN, pipe, map wrappers) | Functions with a single known input type |
| Your team is on Mule 4.5+ | Mixed runtime versions with no upgrade path |
100 patterns with MUnit tests: github.com/shakarbisetty/mulesoft-cookbook
60-second video walkthroughs: youtube.com/@SanThaParv
Top comments (0)