Skip to main content

Control Flow

Skittles supports all standard TypeScript control flow patterns. Use them exactly as you would in any TypeScript project.

If / Else

if (amount > 0) {
this.balances[to] += amount;
} else {
throw new Error("Invalid amount");
}

For Loops

Standard for loops:

for (let i: number = 0; i < this.owners.length; i++) {
this.balances[this.owners[i]] = 0;
}

For...of Loops

for...of loops work on arrays:

for (const owner of this.owners) {
this.balances[owner] = 0;
}

For...in Loops

for...in loops iterate over all values of an enum:

enum Status { Active, Paused, Stopped }

for (const status in Status) {
// Runs once for each enum member
}

The compiler desugars this into a standard indexed loop over the enum range.

While Loops

while (this.count > 0) {
this.count -= 1;
}

Do/While Loops

do {
this.count -= 1;
} while (this.count > 0);

Break and Continue

Both break and continue are supported inside loops:

for (let i: number = 0; i < this.owners.length; i++) {
if (this.owners[i] == target) {
break;
}
if (this.balances[this.owners[i]] == 0) {
continue;
}
// ...
}

Switch / Case

switch (status) {
case VaultStatus.Active:
this._processActive();
break;
case VaultStatus.Paused:
this._processPaused();
break;
default:
throw new Error("Unknown status");
}

Ternary Operator

The conditional (ternary) operator:

let result: number = amount > 0 ? amount : 0;

Void ternary expressions (used as statements for side effects) are also supported and compile to if/else:

condition ? this.doA() : this.doB();

Array Destructuring

Array destructuring is supported for local variable declarations:

const [a, b, c] = [7, 8, 9];

Conditional destructuring is also supported:

let [x, y] = condition ? [a, b] : [b, a];

Tuple Destructuring

You can destructure tuple return values from function calls:

const [r0, r1] = this.getReserves();

This compiles to Solidity's native tuple destructuring syntax: (uint256 r0, uint256 r1) = getReserves();

Object Destructuring

Object destructuring is supported for struct fields:

const { amount, timestamp } = this.getStakeInfo(account);

Direct object literal destructuring also works:

const { a, b } = { a: 1, b: 2 };

Delete

The delete operator resets values in your contract's storage (useful for clearing mapping entries):

delete this.balances[account];

You can also use the Map .delete() method:

this.balances.delete(account);

Try / Catch

Use try/catch to gracefully handle failures from external contract calls. The first statement in the try block must be an external contract call:

try {
const balance: number = this.token.balanceOf(account);
this.lastBalance = balance;
} catch (e) {
this.lastBalance = 0;
}

If the external call fails, execution jumps to the catch block. This is useful for composable DeFi protocols that need to handle failures in other contracts.

You can also use try/catch without capturing a return value:

try {
this.token.transfer(to, amount);
} catch (e) {
this.failed = true;
}