Skip to main content

Type System

Ballerina uses a structural type system where compatibility is determined by the structure of values rather than by declared type names. This design is particularly suited for integration scenarios where data from heterogeneous systems must be validated, transformed, and routed.

Basic types

Simple types

TypeDescriptionExample
int64-bit signed integerint count = 42;
float64-bit IEEE 754 floating pointfloat rate = 3.14;
decimalArbitrary-precision decimaldecimal price = 99.99d;
booleanBoolean value (true or false)boolean active = true;
stringImmutable sequence of Unicode code pointsstring name = "WSO2";
byteInteger in the range 0-255byte b = 0xFF;
nilThe single value () representing absence() nothing = ();

Integer subtypes

Ballerina provides constrained integer types for specific ranges:

SubtypeRange
int:Signed8-128 to 127
int:Signed16-32768 to 32767
int:Signed32-2147483648 to 2147483647
int:Unsigned80 to 255 (same as byte)
int:Unsigned160 to 65535
int:Unsigned320 to 4294967295

String subtypes

SubtypeDescription
string:CharA single Unicode code point

Sequence types

Arrays

Ordered, variable-length collections of a single element type.

int[] numbers = [1, 2, 3];
string[] names = ["Alice", "Bob"];
int[][3] matrix = [[1, 2, 3], [4, 5, 6]]; // array of fixed-length arrays

Tuples

Fixed-length sequences where each position can have a different type.

[string, int] pair = ["age", 30];
[string, int, boolean] record = ["Alice", 30, true];
[int, string...] openTuple = [1, "a", "b"]; // rest type

Structural types

Records

Named collections of fields. Records are open by default, allowing extra fields unless declared as closed with {|...|}syntax.

// Open record (allows additional fields)
type Customer record {
string name;
string email;
int age?; // optional field
};

// Closed record (no extra fields allowed)
type Order record {|
int id;
string product;
decimal amount;
readonly string status; // immutable field
|};

Maps

Collections of key-value pairs where keys are strings and values are a uniform type.

map<string> headers = {
"Content-Type": "application/json",
"Authorization": "Bearer token123"
};

map<json> metadata = {};

Tables

Collections of records with key-based access, similar to database tables.

type Employee record {|
readonly int id;
string name;
string department;
|};

table<Employee> key(id) employees = table [
{id: 1, name: "Alice", department: "Engineering"},
{id: 2, name: "Bob", department: "Marketing"}
];

Union types

A value that can be one of several types. Union types are central to Ballerina's approach to error handling and data validation.

int|string identifier = "ABC-123";
string|error result = doSomething();
int|float|decimal number = 42;

Optional types

The shorthand T? is equivalent to T|(), meaning the value is either of type T or nil.

string? optionalName = ();      // nil
string? presentName = "Alice"; // string value

Singleton types

A type with exactly one value, often used in union types for tagged variants.

type StatusOK "OK";
type StatusError "ERROR";
type Status StatusOK|StatusError;

Status s = "OK";

any and anydata

TypeDescription
anyAny value except error. Equivalent to the union of all types except error.
anydataAny pure data value (no behavioral types). Includes (), boolean, int, float, decimal, string, xml, and structured values made of anydata.

anydata is the type used for values that need to be serialized (JSON, XML, etc.), making it fundamental to integration work.

JSON and XML types

JSON type

json is a union type: ()|boolean|int|float|decimal|string|json[]|map<json>. It represents any value that can be serialized to JSON.

json payload = {
"name": "Order",
"items": [
{"id": 1, "qty": 5},
{"id": 2, "qty": 3}
]
};

// Access with field access
json name = payload.name;

// Access with optional field access (returns nil if missing)
json? discount = payload?.discount;

XML type

xml is a built-in sequence type for XML data, including elements, text, comments, and processing instructions.

xml greeting = xml `<greeting>Hello</greeting>`;
xml catalog = xml `<items>
<item id="1">Widget</item>
<item id="2">Gadget</item>
</items>`;

// XML subtypes
xml:Element element = xml `<name>Alice</name>`;
xml:Text text = xml `plain text`;
xml:Comment comment = xml `<!--a comment-->`;
xml:ProcessingInstruction pi = xml `<?target data?>`;

// Navigation
xml items = catalog/<item>;
string id = (catalog/<item>).id;

Error type

error is a distinct basic type representing error conditions. Every error carries a message (string), an optional cause (error?), and an optional detail record.

// Basic error
error simpleErr = error("something went wrong");

// Error with detail
type OrderError error<record {| string orderId; int code; |}>;
OrderError err = error("order failed", orderId = "ORD-123", code = 404);

// Accessing error fields
string msg = err.message();
record {| string orderId; int code; |} detail = err.detail();

Type definitions and aliases

// Type alias
type StringOrNil string?;

// Distinct type (creates a new type identity for error types)
type ConnectionError distinct error;
type TimeoutError distinct error;

Type narrowing

Ballerina supports flow-sensitive typing. After a type check, the compiler narrows the variable's type within the corresponding branch.

is expression

function process(int|string|error value) {
if value is int {
// value is narrowed to int here
int doubled = value * 2;
} else if value is string {
// value is narrowed to string here
string upper = value.toUpperAscii();
} else {
// value is narrowed to error here
string msg = value.message();
}
}

Match statement

function describe(json value) returns string {
match value {
var n if n is int => { return "integer: " + n.toString(); }
var s if s is string => { return "string: " + s; }
var a if a is json[] => { return "array of " + a.length().toString(); }
_ => { return "other"; }
}
}

check expression

The check expression narrows a union type by extracting the success value. If the value is an error, it immediately returns the error from the enclosing function.

function getUser(int id) returns User|error {
// If query returns error, the function returns that error
User user = check db->queryRow(`SELECT * FROM users WHERE id = ${id}`);
return user;
}

Type compatibility

Because Ballerina uses structural typing, type compatibility is based on value shapes rather than type names.

type Point record {|
int x;
int y;
|};

type Coordinate record {|
int x;
int y;
|};

Point p = {x: 1, y: 2};
Coordinate c = p; // Valid: same structure

readonly types

Any value can be made deeply immutable by intersecting with readonly:

readonly & record {| string name; int age; |} person = {name: "Alice", age: 30};
// person.name = "Bob"; // compile-time error

never type

The never type has no values. It is used for functions that never return (e.g., infinite loops or guaranteed panics) and in type narrowing for impossible cases.

function failed() returns never {
panic error("fatal error");
}