The Adaptavist Group LogoDocumentation

Groovy vs TypeScript Basics

Compare core Groovy and TypeScript concepts side by side, from variables, objects, and array methods to imports, error handling, and HAPI syntax.

If you're coming from Groovy, you'll notice TypeScript is similar in many ways, but with some important syntax differences.

Syntax quick reference table

Use this table for quick lookups when translating Groovy code to TypeScript. You can also use the TypeScript Syntax Quick Reference page to support you when creating and translating your code.

ConceptGroovyTypeScript
Variablesdef x = 5const x = 5
String interpolation"Hello ${name}"`Hello ${name}`
Arraysdef items = [1, 2, 3]const items = [1, 2, 3]
Objects/Mapsdef map = [key: 'value']const map = { key: 'value' }
Functionsdef greet(name) { ... }const greet = (name) => { ... }
Closuresitems.each { item -> ... }for (const item of items) { ... }
Map/Transform.collect { it * 2 }.map(n => n * 2)
Filter.findAll { it > 5 }.filter(n => n > 5)
Sum.sum().reduce((a, b) => a + b, 0)
Null-safe accessuser?.nameuser?.name
Default valuevalue ?: 'default'value ?? 'default'
Equalityx == yx === y
Logginglogger.info('msg')console.log('msg')
Async operationsSynchronousawait required
ImportsAuto-importedimport * as Issues from '@avst-sr/hapi/issues'

Variables and constants

TypeScript uses const and let to declare variables. Unlike Groovy's def, TypeScript distinguishes between values that can change and those that can't. Always use const unless you specifically need to reassign the variable. This makes your code more predictable and easier to debug.

TypeScript:

// const - cannot be reassigned (use this by default)
const name = 'John'
const age = 30

// let - can be reassigned when needed
let counter = 0
counter = 1 // OK

Groovy equivalent:

def name = 'John' // Can be reassigned
final name = 'John' // Cannot be reassigned (like const)

String interpolation

TypeScript uses backticks (`) for string interpolation, similar to Groovy's GString syntax. Regular strings with single or double quotes don't support interpolation.

TypeScript:

const name = 'John'
const age = 30

// Template literal with interpolation
const message = `Hello, ${name}! You are ${age} years old.`

Groovy equivalent:

// Groovy - use double quotes
def name = 'John'
def age = 30
def message = "Hello, ${name}! You are ${age} years old."

Data types

TypeScript has the same basic data types as Groovy, but with slightly different syntax for some of them. TypeScript doesn't distinguish between integers and decimals; they're all just numbers. Also, TypeScript has both null and undefined, whereas Groovy primarily uses null.

TypeScript:

// String
const text = 'Hello world'

// Number (both integers and decimals)
const integer = 42
const decimal = 3.14

// Boolean
const isActive = true
const isComplete = false

// Null and undefined
const empty = null
const notDefined = undefined

// Array (like Groovy lists)
const items = ['apple', 'banana', 'orange']
const numbers = [1, 2, 3, 4, 5]

// Object (like Groovy maps)
const person = {
  name: 'John',
  age: 30,
  active: true
}

Objects and maps

Objects in TypeScript are like Groovy maps, but they use different syntax. TypeScript objects use { key: value } syntax, while Groovy maps use [ key: value ]. This is one of the most visible differences between the languages.

TypeScript:

// Creating objects
const issue = {
  key: 'JRA-1',
  summary: 'Fix bug',
  priority: 'High'
}

// Accessing properties (two ways)
console.log(issue.key)           // Dot notation (preferred)
console.log(issue['summary'])    // Bracket notation

// Adding properties
issue.status = 'Open'

// Nested objects (very common in HAPI)
const issueData = {
  fields: {
    project: { key: 'JRA' },
    issuetype: { name: 'Task' }
  }
}

// Accessing nested properties
console.log(issueData.fields.project.key)  // 'JRA'

Groovy equivalent:

def issue = [
  key: 'JRA-1',
  summary: 'Fix bug',
  priority: 'High'
]

// Accessing properties
issue.key
issue['summary']

Arrays and lists

Arrays in TypeScript work almost identically to Groovy lists, with similar methods for manipulation. The key differences are that TypeScript uses .map() instead of .collect(), and .filter() instead of .findAll(). The for...of loop is the modern way to iterate.

TypeScript:

// Creating arrays
const items = ['apple', 'banana', 'orange']

// Accessing elements
console.log(items[0])  // 'apple'

// Array length
console.log(items.length)  // 3

// Adding elements
items.push('grape')

// Iterating arrays (modern way)
for (const item of items) {
  console.log(item)
}

// Common array methods
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map(n => n * 2)        // [2, 4, 6, 8, 10]
const evens = numbers.filter(n => n % 2 === 0) // [2, 4]
const sum = numbers.reduce((a, b) => a + b, 0) // 15

Groovy equivalent:

def items = ['apple', 'banana', 'orange']
items[0]
items.size()
items << 'grape'

items.each { item ->
 println item
}

def numbers = [1, 2, 3, 4, 5]
def doubled = numbers.collect { it * 2 }
def evens = numbers.findAll { it % 2 == 0 }
def sum = numbers.sum()

Array methods (collections)

Array manipulation methods have different names but similar functionality. For example:

TypeScript:
const numbers = [1, 2, 3, 4, 5]
 
// Transform elements
const doubled = numbers.map(n => n * 2)  // [2, 4, 6, 8, 10]
 
// Filter elements
const evens = numbers.filter(n => n % 2 === 0)  // [2, 4]
 
// Sum all elements
const sum = numbers.reduce((a, b) => a + b, 0)  // 15
 
// Find first match
const first = numbers.find(n => n > 3)  // 4
 
// Check if any match
const hasEven = numbers.some(n => n % 2 === 0)  // true
 
// Check if all match
const allPositive = numbers.every(n => n > 0)  // true
Groovy equivalent:
def numbers = [1, 2, 3, 4, 5]
 
// Transform elements
def doubled = numbers.collect { it * 2 }
 
// Filter elements
def evens = numbers.findAll { it % 2 == 0 }
 
// Sum all elements
def sum = numbers.sum()
 
// Find first match
def first = numbers.find { it > 3 }
 
// Check if any match
def hasEven = numbers.any { it % 2 == 0 }
 
// Check if all match
def allPositive = numbers.every { it > 0 }
Translation guide:
GroovyTypeScript
.collect { }.map()
.findAll { }.filter()
.sum().reduce((a, b) => a + b, 0)
.find { }.find()
.any { }.some()
.every { }.every()

Functions and closures

TypeScript has arrow functions ( =>), which are more concise than traditional function syntax. These are the modern way to write functions in TypeScript.

TypeScript:

const greet = (name) => {
 return `Hello, ${name}!`
}

// Implicit return (concise)
const greet = (name) => `Hello, ${name}!`

// Multiple parameters
const add = (a, b) => a + b

// No parameters
const sayHello = () => console.log('Hello!')

Groovy equivalent:

def greet = { name ->
 return "Hello, ${name}!"
}

// Implicit return
def greet = { name -> "Hello, ${name}!" }

// Multiple parameters
def add = { a, b -> a + b }

// No parameters
def sayHello = { println 'Hello!' }

Iteration patterns

TypeScript has several loop types. The for...of loop is the modern way to iterate over arrays and replaces Groovy's .each() method. It's more flexible because you can use break and continue.

TypeScript:

 // for...of - iterate over array values (use this)
for (const item of items) {
  console.log(item)
}

// Traditional for loop (when you need the index)
for (let i = 0; i < items.length; i++) {
  console.log(items[i])
}

// while loop
let count = 0
while (count < 5) {
  console.log(count)
  count++
}

Groovy equivalent:

items.each { item ->
 println item
}

for (int i = 0; i < items.size(); i++) {
 println items[i]
}

def count = 0
while (count < 5) {
 println count
 count++
}

Conditionals and operators

Conditional logic is very similar between Groovy and TypeScript, with a few operator differences:

  • Use === for equality (not ==)
  • Use ?? for default values (not ?:)
  • Optional chaining ( ?.) works the same way

TypeScript:

if (priority === 'High') {
  console.log('Urgent!')
} else if (priority === 'Medium') {
  console.log('Normal')
} else {
  console.log('Low priority')
}

// Ternary operator (same as Groovy)
const message = isActive ? 'Active' : 'Inactive'

// Nullish coalescing (default values)
const value = userInput ?? 'default value'

// Optional chaining (safe navigation)
const assigneeName = issue.fields.assignee?.displayName

Groovy equivalent:

if (priority == 'High') {
  logger.info('Urgent!')
} else if (priority == 'Medium') {
  logger.info('Normal')
} else {
  logger.info('Low priority')
}

// Ternary operator
def message = isActive ? 'Active' : 'Inactive'

// Elvis operator (default values)
def value = userInput ?: 'default value'

// Safe navigation
def assigneeName = issue.fields.assignee?.displayName

Imports and exports

Unlike Groovy, TypeScript requires explicit imports for all modules. Nothing is auto-imported, so you must declare what you're using at the top of your script. This makes dependencies clear but requires more boilerplate.

TypeScript:

// Importing HAPI modules
import * as Issues from '@avst-sr/hapi/issues'
import * as Projects from '@avst-sr/hapi/projects'

// Importing the REST API client
import { client } from '@avst-sr/jira-client'

// Your script entry point (required)
export default async function () {
 // Your code here
}

Groovy equivalent:

// In Groovy, common classes are auto-imported
// You can just start using Issues, Projects, etc.

// Your script code directly here
def issue = Issues.getByKey('JRA-1')

Error handling

Error handling in TypeScript works identically to Groovy, using the same try/catch/finally structure. The main difference is using console.log() instead of logger.info().

TypeScript:

try {
 const issue = await Issues.getByKey('JRA-1')
  console.log(issue.key)
} catch (error) {
  console.error('Failed to get issue:', error)
 throw error  // Re-throw if needed
} finally {
  console.log('Cleanup code here')
}

Groovy equivalent:

try {
 def issue = Issues.getByKey('JRA-1')
  logger.info(issue.key)
} catch (Exception e) {
  logger.error('Failed to get issue:', e)
 throw e
} finally {
  logger.info('Cleanup code here')
}
 

Groovy closures vs TypeScript objects in HAPI

This is one of the most significant differences when working with HAPI. Groovy uses closures with setter methods, while TypeScript uses nested object literals. This is the most visible difference when working with HAPI. Instead of calling setter methods in a closure, you build a nested object structure. It's more verbose but also more explicit about the data structure.

TypeScript:

await Issues.create({
  body: {
    fields: {
      project: { key: 'JRA' },
      issuetype: { name: 'Task' },
      summary: 'Test issue',
      priority: { name: 'High' },
      description: 'This is a description',
      labels: ['label1', 'label2']
    }
  }
})

// Updating an issue
await Issues.update({
  issueIdOrKey: 'JRA-1',
  body: {
    fields: {
      summary: 'Updated summary',
      priority: { name: 'Medium' }
    }
  }
})

Groovy equivalent:

Issues.create('JRA', 'Task') {
    setSummary('Test issue')
    setPriority('High')
    setDescription('This is a description')
    setLabels('label1', 'label2')
}

// Updating an issue
def issue = Issues.getByKey('JRA-1')
issue.update {
    setSummary('Updated summary')
    setPriority('Medium')
}

Search documentation

Start typing to search the docs.