as

Settings
Sign out
Notifications
Alexa
Amazon Appstore
AWS
Documentation
Support
Contact Us
My Cases
Get Started
Design and Develop
Publish
Reference
Support

JavaScript Types in Native Code

Vega Turbo Modules support a variety of native types which can be converted to and from JavaScript in Turbo Module parameter and return values. This means that, for example, a Turbo Module method which accepts std::string can be called from JavaScript with a JavaScript string, and the argument will be automatically converted; similarly, a Turbo Module method which returns std::string will return a JavaScript string to the JavaScript environment. Some of these, such as std::string and int32_t, are basic C++ types. Others are specific to Vega, and are primarily available as part of the com::amazon::kepler::turbomodule namespace.

Types map

The following table shows the mapping between TypeScript types and their C++ counterparts. This is the mapping used by the codegen tool.

Some of these types are not part of base TypeScript and need to be imported from @amazon-devices/keplerscript-turbomodule-api.

TypeScript type Native type
string std::string
number double
Int32 int32_t
UInt32 uint32_t
Int64[1] int64_t
Double double
Float float
boolean bool
void void
Array<T>, T[] (homogeneous)[2] std::vector<T>
Array JSArray
Object JSObject
Promise Promise
Function (parameter type) Callback
Function (return type) HostCallback
bigint BigInt[3]
ArrayBuffer ArrayBuffer
NativeObject std::shared_ptr<NativeObject>
*JsonContainer[4] utils::json::JsonContainer

[1] int64_t might have loss of precision due to Number.MAX_SAFE_INTEGER. You can use BigInt for large integers where the precision is necessary.

[2] A homogeneous array is the one whose elements are of the same type.

[3] BigInt is supported for signed and unsigned 64-bit integers, as direct method parameter and return types. For more details on supported usages, see BigInt

[4] JsonContainer will be removed after the gaps in JSObject are addressed. If you need to use it, you can import JsonContainer type from @amazon-devices/keplerscript-turbomodule-api for a generic Object, or define a custom type alias such as

Copied to clipboard.

type MyJsonContainer = {myProperty: string};

Note: For integers larger than JavaScript's Number.MAX_SAFE_INTEGER, use a std::string representation for the Turbo Module's parameter and return types. You can convert the string to more appropriate types within the JavaScript and C++ implementations.

JSArray

For homogeneous (T[]) arrays, you can use std::vector<T>. For other arrays, you can use JSArray.

A JSArray is an std::vector which accepts a variety of element types including null pointer, boolean, various int, double, float, std::string, JSArray, and JSObject. Its usage is therefore similar to std::vector.

Example

Copied to clipboard.

JSArray SampleTurboModule::getJSArray() {
    auto jsObject = JSObject{};
    auto jsArray = JSArray{};

    jsArray.emplace_back(jsObject);
    jsArray.emplace_back(std::string{"foo"});
    jsArray.emplace_back(3.14);

    return jsArray;
}

JSArray SampleTurboModule::extendJSArray(JSArray arr) {
  arr.emplace_back(std::string{"new element"});
  return arr;
}

JSObject

A JSObject is an std::map with std::string keys which accepts a variety of value types including null pointer, boolean, various int, double, float, std::string, JSArray, and JSObject. Its usage is therefore similar to std::map.

JSObject provides a static parseJson method. The parseJson method accepts an std::string JSON string and returns the equivalent JSObject.

Example

Copied to clipboard.

JSObject SampleTurboModule::getJSObject(std::string jsonContent) {
    auto jsObject = JSObject::parseJson(jsonContent);
    return jsObject;
}

JSObject SampleTurboModule::getJSObject() {
    auto jsObject = JSObject{};

    jsObject.emplace("intValue", 100);
    jsObject.emplace("doubleValue", 25.1);
    jsObject.emplace("boolValue", true);
    jsObject.emplace("strValue", std::string{"string value"});

    return jsObject;
}

JSObject SampleTurboModule::extendJSObject(JSObject obj) {
    obj.emplace("foo", std::string{"bar"});
    return obj;
}

Proxy JSObject and JSArray

Vega Turbo Modules support the proxy types proxy::JSObject and proxy::JSArray in addition to JSObject and JSArray types. One of the key features of the proxy types is dynamic access and modification. You can read from and write to JavaScript objects and arrays directly from your C++ code, without having to create copies.

However, there are a few important things to keep in mind when working with these proxy types:

  • Thread Considerations:

    The proxy types must be used within the JSThread context and the scope of a Turbo Module method invocation. They can't be stored or accessed from arbitrary threads. Thread safety checks are only performed in Debug mode, so be cautious when working with these proxy objects in a multi-threaded environment.

  • Native construction:

    You can natively construct new proxy items, but you'll need to obtain the required JSRuntime as a method parameter from your Turbo Module or HostCallback methods.

    Warning: Don't use default constructors. the default constructors for these proxy types are only maintained to support current internal usage in constructs such as std::tuple and std::variant. You shouldn't rely on these default constructors, as they require a JSThread context and will be removed in a future release.

JSRuntime

The JSRuntime type represents a JavaScript runtime environment. It's a native-only type that can be requested as a parameter by any Turbo Module or HostCallback method. It is typically used for creating new proxy items in C++.

Note: JSRuntime cannot be constructed from a non-JSThread.

Copied to clipboard.

void SampleTurboModuleV2::sampleMethod(JSRuntime rt) {
    // Creating a new proxy object
    auto newObject = proxy::JSObject(rt);

    // Creating a proxy array
    auto newArray = proxy::JSArray(rt);
}

proxy::JSObject

The proxy::JSObject type allows you to create a JavaScript object that can be passed to the native code, and have its properties and methods dynamically accessed and modified from the native side without creating a copy.

Note: Access to proxy::JSObject is restricted to the JSThread context and must occur within the scope of a Turbo Module method invocation. Thread safety checks are only performed in Debug mode.

Copied to clipboard.

std::vector<proxy::JSObject> SampleTurboModuleV2::getProxyObject(JSRuntime rt, std::vector<proxy::JSObject> args) {
    // Reading properties from received object
    auto object = args[0];
    auto propertyNames = object.getPropertyNames();
    for (const auto& propertyName: propertyNames) {
        auto propertyValue = object.getProperty(propertyName);
        if (std::holds_alternative<std::string>(propertyValue)) {
            auto stringValue = std::get<std::string>(propertyValue);
            TMINFO("Property " + propertyName + " = " + stringValue);
        }
    }

    // Creating and returning new object
    auto newObject = proxy::JSObject(rt);
    newObject.setProperty("stringProp", "foo");
    newObject.setProperty("numberProp", 1.5);
    newObject.setProperty("boolProp", true);

    args[0] = newObject;
    return args;
}

proxy::JSArray

The proxy::JSArray type allows you to create a JavaScript array that you can pass to the native code, and have its elements dynamically accessed and modified from the native side.

Note: Access to proxy::JSArray is restricted to the JSThread context and must occur within the scope of a Turbo Module method invocation. Thread safety checks are only performed in Debug mode.

Copied to clipboard.

proxy::JSArray SampleTurboModuleV2::getProxyArray(proxy::JSArray arg) {
    size_t index = 0;
    for(const auto& element: arg) {
        if (std::holds_alternative<std::string>(element)) {
            auto elementValue = std::get<std::string>(element);
            TMDEBUG("SampleTurboModuleV2::getProxyArray initial string element = " + elementValue);
            arg.set(index, "C++String");
        }
        ++index;
    }
    return arg;
}

Promise

Promises should be executed on a separate thread.

A Promise is constructed with a PromiseOperation as input. A PromiseOperation is a function which accepts an std::shared_ptr<Promise> and returns void.

A Promise has resolve and reject methods. A Promise can be rejected with an Error or std::string, and resolved with any of the C++ Turbo Module types from the types map.

Example

Copied to clipboard.

Promise<std::string> SampleTurboModule::getValueWithPromise(bool error) {
    return Promise([self = this, error](const std::shared_ptr<Promise>& promise) {
        std::thread([error, promise]() {
            if (error) {
                promise->reject(Error("promise rejected using Error class"));
            } else {
                promise->resolve(std::string{"result!"});
            }
        }).detach();;
    });
}

Callback

A callback should be executed on a separate thread.

A Callback object can only be a parameter value, representing a JavaScript callback passed to C++. See HostCallback for callbacks as a return type.

The only method exposed by the Callback object is invoke which calls the JavaScript callback with the specified arguments, and returns the result to C++.

Example

Copied to clipboard.

// JS
SampleTurboModule.getValueWithCallback((value : number) => {
    console.log(value); // this should log the value 5, which is passed as an argument from the C++ Callback.invoke method.    
    return 2 * value;
});

// C++
void SampleTurboModule::getValueWithCallback(Callback callback) {
    std::thread([](Callback callback) {
        auto future = callback.invoke(5);
        std::string returnValue{"undefined"};
        try {
         auto jsValue = future.get();
         // Any number returned from JS is always a double
         if(std::holds_alternative<double>(jsValue)) {
           returnValue = std::to_string(std::get<double>(jsValue));
         }
        } catch (const std::exception& ex) {
         TMERROR("Error getting the return value from callback. Exception: " + std::string(ex.what()));
        }
        TMINFO("Return value from callback is: " + returnValue);
    }, callback).detach();
} 

HostCallback

HostCallback can only be a return value, used to return a C++ callback to the JavaScript environment. See Callback for callbacks as a parameter type.

Example

Copied to clipboard.

// C++
std::string sampleHostCallbackMethod(std::string jsInputString) {
    return "Received JS string " + jsInputString);
}

HostCallback SampleTurboModule::getHostCallback() {
    return HostCallback(sampleHostCallbackMethod);
}

// JS
const hostCallback = SampleTurboModule.getHostCallback();
console.log(hostCallback("Hello from HostCallback!")); // prints "received JS string Hello from HostCallback!"
}

You can also invoke a Callback using a HostCallback.

Copied to clipboard.

// JS
SampleTurboModule.invokeCallbackWithHostCallback((hostCallback: (obj: Object) => number): void {
    const secret = hostCallback();
    console.log(secret); // logs 17
});

// C++
void SampleTurboModule::invokeCallbackWithHostCallback(Callback callback) {
    auto secret = 17;
    auto lambda = [secret]() {
        return secret;
    };
    auto hostCallback = HostCallback(std::function<int()>(lambda));

    callback.invoke(hostCallback);
}

ArrayBuffer

An ArrayBuffer represents a generic, fixed-length raw binary data buffer.

Example

Copied to clipboard.

ArrayBuffer SampleTurboModule::getNativeMemoryArrayBuffer() {
    return {"Hello World!"};
}

ArrayBuffer SampleTurboModule::modifyArrayBuffer(ArrayBuffer buf) {
    // No copy of data since it's just modification without inserting new content
    uint8_t* data = buf.data();
    size_t size = buf.size();
    for (auto i = 0; i < size; i++) {
        data[i] = 9;
    }

    return buf;
}

ArrayBuffer SampleTurboModule::insertSecretData(ArrayBuffer buf) {
    // Insert here creates a copy of the data and ataches a finalizer
    uint8_t secret[] = {6, 7, 9, 9, 0};
    arg.insert(5, secret);
    return arg;
}

BigInt

A BigInt represents an integer. In JavaScript, these are used for integers which exceed Number.MAX_SAFE_INTEGER or Number.MIN_SAFE_INTEGER. In Vega, Turbo Modules support BigInt with int64_t- and uint64_t-based getter and constructor methods.

Copied to clipboard.

BigInt SampleTurboModule::getBigInt(BigInt bigint) {
    // Create BigInts
    auto big1 = BigInt::fromI64(-9007199254740990);
    auto big2 = BigInt::fromUi64(static_cast<uint64_t>(9007199254740990));

    // Access values. Getters are not dependent on the type used in construction.
    auto asI64 = bigint.getI64();
    auto asUi64 = bigint.getUi64();

    // getI64() returns a struct with the following properties
    int64_t i64value = asI64.value;
    bool lossless = asI64.lossless;

    // getUi64() returns a struct with the following properties
    bool is_signed = asUi64.is_signed;
    uint64_t ui64value = asUi64.value;
    bool lossless = asUi64.lossless;

    return big1;
}

You can use BigInt in method parameter and returns, as well as Callbacks and Promises, but only as a raw value and not as a part of a JSArray or JSObject.

A JavaScript BigInt which is too large to be represented by the Vega Turbo Module BigInt type (for example, it cannot be represented by int64_t or uint64_t) will fail an assertion, as do all invalid parameters for Vega Turbo Modules. For more information, see the error handling in "Advanced Turbo Module topics".

utils::json::JsonContainer

Use of JsonContainer for the JSObject type is being replaced, but there are currently still some feature gaps. For a full list of supported types, see the types map.

Example

Copied to clipboard.

utils::json::JsonContainer SampleTurboModuleV2::getJsonContainer() {
    auto result = utils::json::JsonContainer::createJsonObject();
    result.insert("const1", true);
    result.insert("const2", 375);
    result.insert("const3", std::string("something"));
    return result;
}

NativeObject

In most cases, when you are passing data from your native code to JavaScript, a JSONContainer or JSObject will suffice. However, if you need a native object whose methods can be invoked from JavaScript, you need to create an object that implements com::amazon::kepler::turbomodule::NativeObject.

Example

Copied to clipboard.

#include <Vega/turbomodule/NativeObject.h>

using namespace com::amazon::kepler::turbomodule;

class SampleNativeObject : NativeObject {
public:
    // Methods must be added to the methodAggregator in this function in order to be exposed to JavaScript.
    void aggregateMethods(MethodAggregator<NativeObject> &methodAggregator) noexcept override {
        methodAggregator.addMethod("getString", 1,
                                   &SampleNativeObjectV2::getString);
    }
    std::string getString(std::string arg) {
        return "Goodbye " + arg;
    }
};

// in TurboModule implementation:
std::shared_ptr<NativeObject> SampleTurboModule::getNativeObject() {
    return std::make_shared<SampleNativeObject>();
}

// Usage in JavaScript:
SampleTurboModule.getNativeObject().getString("foo"); // returns "Goodbye foo"

Note that NativeObject is only supported as a Turbo Module method return type, not a parameter.

APMF Types

IDLs use some APMF types which are not always directly convertible between the supported Turbo Module parameter/return types. You might need to implement custom logic to do these conversions.

Create a Turbo Module


Last updated: Sep 30, 2025