Static Type Checking

Static type checking (STC) provides information about whether your script is correctly written.

Groovy is a dynamic language, which means that method and property names are looked up when your code is run, not when it’s compiled (like Java).

Let’s look at the following simple, but complete, script:

foo.bar()

We call the method bar() on the object foo. This script compiles without errors, but you get a MissingPropertyException when you run the script because foo hasn’t been defined. This behaviour is useful because there are circumstances that could make this code execute successfully, like an object called foo or a closure getFoo() being passed to the script’s binding.

Although Groovy is a dynamic language, we can compile scripts in a manner that checks method and property references at compilation. The STC feature shows you problems in your scripts when you are writing them, as opposed to when they execute.

When your scripts are executed, they are always compiled dynamically. When they are compiled for the STC, the resulting generated bytecode is thrown away.

Limitations

There are limitations to the type checker. It is possible to write code that shows errors, but it is valid and executes fine. Some of these situations are:

  • Using certain builders

  • Using closures where the parameter types can’t be inferred

However, if you write code like this, you probably use an IDE, which does not work with the STC.

Additionally, your code could have runtime errors that won’t be found until the code executes.

Let us know if you find code that should compile but doesn’t.

Examples

The code for a script fragment condition to check that the user is in a specific space might look like the following:

type checked condition

The STC shows that we are trying to set the space key, rather than retrieve it. We should have used the equality operator:

type checked condition ok

Note the green dot, which tells us that the code is syntactically correct after checking the methods and properties.

Deprecations

The STC also shows methods and properties that are deprecated. Deprecations are parts of the API that Atlassian would prefer you not to use.

In the following image, there is one deprecated method:

type checked condition depr 2

It is advisable to change your code to the suggested alternative because Atlassian usually removes deprecated code on major version releases. If you switch to the non-deprecated code, there is a better chance of your script continuing to work on your next upgrade.

A fixed version of the above script might look like:

type checked condition depr fixed 1

Tips

Fields

To write classes, give information about the field types to the STC.

If you do not, you’ll receive the following STC errors:

type checked condition class depr

Instead, declare your field types like this instead:

import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.applinks.api.ApplicationLinkService

class Foo {
    ApplicationLinkService applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService)

    void doSomething() {
        applicationLinkService.getApplicationLinks()
    }
}

Closures

When writing closures, you may need to provide additional type information.

For example:

import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.applinks.api.ApplicationLinkService

def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService)

applicationLinkService.getApplicationLinks().findAll {
    it.name == "Confluence"
}

It is inferred from the context that the type of it is an ApplicationLink object.

While this code will execute without issues, the STC produces an error: No such property: name for class: java.lang.Object.

Using the following code, you can fix this error by explicitly informing the STC that the type of it is an ApplicatonLink object.

import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.ApplicationLink

def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService)

applicationLinkService.getApplicationLinks().findAll { ApplicationLink it ->
    it.name == "Confluence"
}
Either version of the code is fine. If you want to ignore the STC, you can. The version of the bytecode that is actually executed is always compiled in "dynamic" mode.

If you wanted to take the closure in the previous example and reuse it, you should write code like this:

import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.ApplicationLink

def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService)

def findApplicationByName = { ApplicationLink it -> it.name == "Confluence" }

applicationLinkService.getApplicationLinks().findAll(findApplicationByName)

Type Information

Certain parts of Atlassian’s API are not strongly typed.

For example, the method for cloning certain objects are defined to return a java.lang.Object. The type checker only has access to this information, and it is not aware of the types of objects you’re cloning.

The following script, which retrieves an existing space, clones it, and then attemps to access the key, is flagged as having errors:

type checked customfields
Remember, despite the STC error, the script will still run successfully.

The only information that the Confluence API gives the STC is that clonedSpace has the type of java.lang.Object. Fix this by casting the object returned from the clone to a Space:

type checked customfields fixed

Have questions? Visit the Atlassian Community to connect, share, and learn with other Atlassian users and experts, including Adaptavist staff.

Ask a question about ScriptRunner for JIRA, Bitbucket Server, or Confluence.

Want to learn more? Check out courses on Adaptavist Learn, an online platform to onboard and train new users for Atlassian solutions.