Reading Time: 14 minutes

The type system can help you save time preventing errors, find quickly the function you need, instead of looking for it in the documentation, and let you code using types to define different behaviors for functions. In this post, I’ll show you how the DataWeave type system can help you develop in a more efficient and clear way.

We’ll go over:

  • What is a type system?
  • How it can help you:
    • Avoid potential errors by highlighting them as you are writing your script.
    • Define different behaviors for functions in runtime, based on the type of inputs.
    • Develop faster by using the suggestions of autocompletion.
  • Why the DataWeave script works even when you don’t seem to use its type system.
latest report
Learn why we are the Leaders in API management and iPaaS

Let’s review this in more detail.

Basics of the type system

A type system defines a set of type constraints to a set of constructs, in the case of DataWeave, these constructs are:

  • Variables
    • Syntax for annotating variables with type constraints: 
var varName: TypeConstraint = aValue
  • Functions
    • Syntax for annotating functions with type constraints: 
fun functionName(param1: Type1, param2: Type2): ReturnType = Body

The constraints are used in the type-checking phase, when DataWeave ensures that the values assigned to variables or the arguments for a function call respect its constraints. 

The possible type constraints are:

  • Simple types: 
    • String, Boolean, Number, Regex, Null, Temporal Types
  • Composite types: 
    • Array, Object, Function
  • Complex types: 
    • Any, Nothing, Union Type, Intersection Type, Literal Types 
  • Type declaration:
    • You could declare your own types to reuse them as constraint following the syntax: 
type TypeName = AnotherType

Note: If you are familiar with TypeScript, its type system is pretty similar to the DataWeave one.

Short examples of its usage:

Script:

%dw 2.0
output json
//String constraint
var userName: String = "Julian" 
---
userName

Output:

"Julian"

In this example, the variable definition for userName has the constraint of assigning to it a value of type String

Script:

%dw 2.0
output json
fun welcomeMessage(team: Array, day: Date | DateTime): String = 
    day as String ++ ": Good Morning " ++ (team joinBy ", ") ++ "!!"
---
welcomeMessage(["Mariano", "Shoki", "Ana"], now())

Output:

"2020-10-28T23:12:50.444Z: Good Morning Mariano, Shoki, Ana!!"

The constraints to call the function welcomeMessage are that the first argument has to be of type Array of String and the second one of type Date or DateTime (or to be coercible to those types because DataWeave supports auto-coercion for function calls). Another constraint is that the result of the function has to be of type String. DataWeave validates that the function body generates the proper type.

Script:

%dw 2.0
output json
//Declaration of a new type User with an Object constraint
type User = {"name": String, "age": Number}
//Usage of the type User as a constraint
var someUser: User = {"name": "Lautaro", "age": 41}
---
someUser

Output:

{
 "name": "Lautaro",
 "age": 41
}

The var someUser has the constraint of assigning to it a value of type User, that is a custom type Object with the keys “name” and “age” defined and with values of type String and Number respectively.

Benefits of using the type system

Prevents type checking errors before executing:

DataWeave is statically type-checked, this means that before the execution of the script, every variable and function has a constraint (a type) assigned to it, and those constraints are used in the type-checking phase, where DataWeave ensures that the values assigned to variables or the arguments for a function call respect its constraints if it doesn’t a type-checking error is raised. 

What it is useful here, is that because the type checking phase runs before the execution of the script, the tooling of the IDE can help you find type checking errors just while writing your script, instead of when the script is running and fails (this could save you a lot of time trying to deploy your app and waiting for the execution of your script to generate the error message).

In this example, you can see that the welcomeMessage function receives an Array of String as a first parameter, and in the function call we pass correctly an array of three strings. Adding an additional element of type Number into that array generates instantly the type checking error in the IDE thanks to the constraint defined.

Function overloading:

Another use of the type system is in the function dispatching algorithm that occurs at runtime. This algorithm uses the constraints when the function is overloaded (when it has multiple definitions for the same function).

%dw 2.0
output json
fun sizeOf(array: Array) = "Code for sizeOf with Arrays"
fun sizeOf(text: String) = "Code for sizeOf with Strings"
---
sizeOf(payload)

Based on the type of the payload and the constraints, the function dispatching algorithm will choose at runtime the correct function to execute. If the payload is an Array, the function dispatching algorithm will choose the first definition, if it is a String it will choose the second definition.

Autocompletion:

Autocompletion could help you develop more efficiently using the types that you declare as constraints to make suggestions more accurate. For example, it can suggest some functions to call based on the parameters types. This is really useful for finding that particular function that you need but maybe you don’t remember or know.

As you can see here, the parameter num has the constraint to be a Number so the Autocompletion tool suggests only the functions that receive as a first parameter a Number. It also shows a description of each function.

Other benefits:

  • One can refactor with more confidence, since a large class of errors introduced during refactoring could end up as type errors, so the tolling will help us detect those errors earlier.
  • Types could serve as documentation for yourself and other programmers, to understand how to modify or use the code.

Type inference

Even if no types are explicitly specified as constraints in your code, the type checking will be running because DataWeave uses a global type inference algorithm to validate your code. It infers the types of the constructs based on the value assigned to it, and validates the constraints of every function that is used in the script. For example, every function of dw::Core has explicit type constraints for its parameters, so when you use them, the type checking will check if those constraints are met.

%dw 2.0
output json
var message= "Hey"
fun addExclamations(text: String): String = text ++ "!!"
---
addExclamations(message)

Here, the variable message hasn’t the type constraint explicit, so the type inference algorithm looks at the value that is being assigned to message, and because that is a String value, is going to declare that the variable message is of type String

When the function addExclamations is called with message, the type checking will check if message is of type String (the constraint for the text parameter), and thanks to the type inference that is going to meet the constraint. Also the call for ++ will be checked. This code generates the output “Hey!!” as expected.

Although using the type inference could imply a less verbose code, there are some benefits of the type system that are lost. In some cases, the type inference could be less restrictive than one expects, resulting in unexpected behaviors of the code.

%dw 2.0
output json
fun addPi(num) = num + 3.14
---
addPi([1]) //No type checking errors for calling it with an Array

In the example above, one could expect an error, but because the inferred type of the parameter num is Any (all the types meet that constraint) and the operator + is defined for the type Array too, this code has not type errors and generates the output [1, 3.14] (probably unexpecting it).

Also, Autocompletion could make some general suggestions instead of the more accurate ones when explicitly writing the type constraint, making the search of the function that you need less effective.

In this example, you can see the difference between the suggestions of Autocompletion when the type constraint is defined and when it isn’t.

Conclusion

The DataWeave type system can help you avoid type checking errors, define different behaviors for functions based on the types constraints, and develop with the tooling more efficiently. 

Check out the latest documentation about the DataWeave type system and to begin using it, signup for a free trial of Anypoint Platform


Series NavigationDataWeave: Working with literal types >>