🚢
Airship
  • Getting Started
    • Welcome to Airship
    • Installing Airship
  • TypeScript
    • Overview
    • AirshipBehaviour
      • Adding Inspector Properties
      • AirshipSingleton
      • Using Component Decorators
      • Accessing Other Components
  • Publishing
    • Publish Game
    • Developer Console
  • Networking
    • Multiplayer
    • Local Server Mode
    • Network Signals
    • Network Functions
    • Network Identity
    • Network Transform
  • Input
    • User Input
      • Actions
      • Keyboard
      • Mouse
    • Proximity Prompts
  • Core Package
    • What is the Core Package?
    • Chat Commands
    • Inventory
    • Spawning Characters
    • Enable / Disable Core Features
  • Physics
    • Physics Settings
    • Physics Layers
  • Platform Services
    • Data Store
      • Locking
    • Cache Store
    • Leaderboards
    • Platform Inventory
    • Server Management
    • Server List
    • Server Transfers
    • Users
    • Parties
    • Matchmaking
    • External Services
  • CHARACTERS
    • Quick Configuration
    • Character Movement System
      • Character Movement Data
      • Character Movement Events
    • Character Camera
      • First Person Camera
      • Simple Usage
      • Camera Structure
      • Default Camera Modes
      • Disabling the Camera System
    • Character Animations
      • Character Blender Animations
      • Character Ragdoll
  • Accessories
    • Accessories Intro
    • Creating Accessories
    • Using Accessories
  • ANIMATIONS
    • Animation Events
  • Optimization
    • Live Game Profiler
    • Reducing Bundle Size
  • Game Settings
    • Game Visibility
  • Other
    • Project Status
    • FAQ
    • DataType Math
    • JS to Luau
    • Tags
    • Terrain
    • AirshipNetworkBehaviour
      • Lifecycle Events
      • ServerRpc
      • ObserversRpc
      • TargetRpc
    • VoxelWorld
      • Voxel World Getting Started
      • Voxel World Tips
      • Prefab Voxels
      • QuarterBlocks
    • Easy Helper Utils
      • Easy Motion
      • Easy Grid Align
      • Easy Look At
      • Easy Shake
      • Easy Destroy
Powered by GitBook
On this page
  • About
  • Console Logging
  • Strings
  • Math
  • ToString
  • ParseInt and ParseFloat
  • Null
  • Throw
  • Iterating custom objects and enums (Object.entries)
  • for..in loops
  • Timeouts and Intervals
  • Clearing Timeouts and Intervals
  • JSON
  • Signed Bitwise operations
  • Passing a method to a function
  • Other
  1. Other

JS to Luau

Equivalent JavaScript functions/libraries in the Airship Luau ecosystem.

PreviousDataType MathNextTags

Last updated 1 month ago

About

Since unity-ts compiles to Luau instead of JavaScript, there are a few gotcha's when expecting a JavaScript function or library to exist. This page should help cover the conversion from JavaScript expectations to Luau.

Console Logging

  • console.log -> print

  • console.warn -> warn

  • console.error -> error

print("Hello world");
print("anything", "can be", "listed", 10, true, false);

warn("This is a warning");

error("This is an error");
print("This won't print because the above error halted execution");

Note: Unlike in JavaScript, calling error will halt execution of the current Luau thread.

Strings

The Luau library is available for use. A few extra functions have been added to the string library to help it reach more equivalency to JavaScript:

  • string.trim(str)

  • string.trimStart(str)

  • string.trimEnd(str)

Math

print(math.floor(32.5)); // -> 32

ToString

Use the global tostring() function to convert any value to a string.

  • obj.toString() -> tostring(obj)

const num = 32.5;
const numAsStr = tostring(num);

ParseInt and ParseFloat

In Luau, all numbers are treated as double floats. As such, there is only one equivalent function to parse values into numbers: tonumber().

  • parseInt(value) -> tonumber(value)

  • parseFloat(value) -> tonumber(value)

const n0 = tonumber("32");
const n1 = tonumber("40.5");

if (typeIs(n0, "number") && typeIs(n1, "number")) {
    print(n0 + n1) // 72.5
}

If the value cannot be converted to a number, nil is returned instead (which we can check as undefined in TypeScript).

Null

The null value is not used in unity-ts. Instead, use undefined. In Luau, this is equivalent to nil.

Throw

Throwing an exception is equivalent to calling the error function. Both compile down to the same Luau code.

// Both equivalent:
throw "Some Error";
error("Some Error");

Iterating custom objects and enums (Object.entries)

Arrays, Maps and Sets will work as expected and are handled by the compiler. Custom objects and enums however work differently.

In Luau, if we want to iterate over the keys/values of objects (e.g. get all the values of an enum) - we use the pairs function. Think of this like the Luau equivalent of Object.entries.

const obj = {
    a: 10,
    b: true,
    c: "Hello, World!",
}

for (const [key, value] in pairs(obj)) {
    // key will be whatever the key type of the object is
    // value will be the value type of the given key on the object
}

export enum ExampleEnum {
    A,
    B,
    C,
}

for (const [key, value] of pairs(ExampleEnum)) {
    // key will be the name of the given enum item (a string)
    // value will be the value of the enum item (string or number, depending on enum)
}

for..in loops

for..in loops are not supported, you should refer to the above documentation for how to grab keys from objects

Timeouts and Intervals

Luau's coroutine system is much different than the event loop found in JavaScript. The task library contains helpful functions for scheduling and spawning new Luau coroutine threads.

SetTimeout

Use task.delay to schedule a new thread to run in the future.

task.delay(3, () => {
    print("Delayed 3 seconds");
});

Using task.wait will also cause a delay; however, it will yield the current thread for the number of seconds, rather than scheduling a new thread for the future.

task.wait(3);
print("Delayed the current thread for 3 seconds");

SetInterval

There is no direct equivalent function to setInterval, but the behavior can be easily reproduced in a few different ways:

// Custom setInterval function:
function setInterval(fn: Callback, interval: number) {
    return task.delay(interval, () => {
        while (true) {
            fn();
            task.wait(interval);
        }
    });
}

setInterval(() => {
    print("tick");
}, 3);
// Simple interval loop:
const interval = 3;
task.spawn(() => {
    while (true) {
        task.wait(interval);
        // Foo
    }
});

Clearing Timeouts and Intervals

Use task.cancel to stop a scheduled or already-running coroutine thread. All thread scheduling functions (task.spawn/defer/delay) return a "thread" object, which can be fed into the cancel function.

const thread = task.delay(3, () => {
    print("Hello?");
});

// Immediately cancel the thread:
task.cancel(thread);
const thread = task.spawn(() => {
    while (true) {
        doSomething();
        task.wait(1);
    }
});

// Stop the above loop after 3 seconds:
task.wait(3);
task.cancel(thread);

JSON

  • JSON.parse -> json.decode

  • JSON.stringify -> json.encode

// Common JavaScript:
const jsonString = JSON.stringify(anyObject);
const obj = JSON.parse(jsonString);

// Airship:
const jsonString = json.encode(anyObject);
const obj = json.decode(jsonString);

Since JSON objects are inherently unknown in shape, json.decode will return an unknown type. If desired, a generic type can be used on the decode function, but note that this does not enforce the type.

interface Hello {
    hello: string;
}

// Generic used as a type hint, but this does not cause any runtime type checking:
const obj = json.decode<Hello>(`{"hello": "world"}`);
print(obj.hello);

Empty Objects and Arrays

In Luau, the table type is used for both objects and arrays. While unity-ts lets you specify between various data structures (objects, arrays, maps, sets), they call compile down into Luau tables.

As such, json.encode cannot differentiate between an empty object and an empty array. Any empty structure will be encoded as an empty JSON array: [].

print(json.encode([])); // []
print(json.encode({})); // []

Signed Bitwise operations

In short, bitwise operations will be equivalent to JavaScript's. However, this is different than what would usually be expected from Luau.

By default, any TypeScript bitwise operations go through the sbit32 library. Luau ships with an unsigned bit32 library, but signed bitwise operations mesh better within Unity, as C#'s bitwise operations are also signed.

In general terms, this means that bitwise operations should be expected to be equivalent to what you would already expect from JavaScript. However, if you are coming from a Luau background, please note the difference here.

The bit32 and sbit32 libraries can also be directly used if desired.

print(19 | -3); // -1
print(sbit32.bor(19, -3)); // -1
print(bit32.bor(19, -3)); // 4294967295

Passing a method to a function

In Luau, methods are handled quite differently to JavaScript. When you call a method, the calling object is passed as the first parameter implicitly (through object:Method(...)) or explicitly through object.Method(object, ...).

In unity-ts, if you try passing a method directly as an argument (e.g. doThingWithAMethod(object.Method)) you will end up with "Cannot index method without calling it!"

The best way to deal with this, is to explicitly "bind" a function to object

// Simple "bound function", like JS' function.bind(object)
const boundFn = () => object.Method();
// and if you need to pass args:
const boundFnWithArgs = (value: string) => object.Method(value);

This will ensure that Luau will call it as a method attached to object when you call the bound function

Other

Instead of the JavaScript Math library, the Luau library is available.

All are available for use.

string
math
Luau libraries