Data Type Conversion Between C/C++ and JavaScript in NAPI Framework

NAPI Data Type Convresion Overview

OpenHarmony NAPI encapsulates ECMAScript standard data types including Boolean, Null, Undefined, Number, BigInt, String, Symbol, Object, and Function into the unified napi_value type. This type handles data exchange between ArkUI applications and native code.

This implementation demonstrates a synchronous Add(num1, num2) interface to illustrate the conversion process.

C++ Implementation

#include <string.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"

static napi_value ComputeSum(napi_env env, napi_callback_info info) {
    size_t expectedArgs = 2;
    size_t actualArgs = 2;
    napi_value parameters[2] = {nullptr};
    
    napi_status status = napi_get_cb_info(env, info, &actualArgs, parameters, nullptr, nullptr);
    if (status != napi_ok) {
        return nullptr;
    }

    napi_valuetype paramType1;
    napi_valuetype paramType2;
    
    napi_typeof(env, parameters[0], &paramType1);
    napi_typeof(env, parameters[1], &paramType2);

    if (paramType1 != napi_number || paramType2 != napi_number) {
        napi_throw_type_error(env, nullptr, "Invalid arguments. Expected 2 numbers.");
        return nullptr;
    }

    double operand1, operand2;
    napi_get_value_double(env, parameters[0], &operand1);
    napi_get_value_double(env, parameters[1], &operand2);

    napi_value result;
    napi_create_double(env, operand1 + operand2, &result);

    return result;
}

static napi_value RegisterModule(napi_env env, napi_value exports) {
    napi_property_descriptor methods[] = {
        {"calculateSum", nullptr, ComputeSum, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    
    napi_define_properties(env, exports, sizeof(methods) / sizeof(methods[0]), methods);
    return exports;
}

static napi_module calculatorModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = RegisterModule,
    .nm_modname = "calculator",
    .nm_priv = nullptr,
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void InitializeModule() {
    napi_module_register(&calculatorModule);
}

Module Registration and Interface Declaration

static napi_value RegisterModule(napi_env env, napi_value exports) {
    napi_property_descriptor methods[] = {
        {"calculateSum", nullptr, ComputeSum, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    
    napi_define_properties(env, exports, sizeof(methods) / sizeof(methods[0]), methods);
    return exports;
}

static napi_module calculatorModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = RegisterModule,
    .nm_modname = "calculator",
    .nm_priv = nullptr,
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void InitializeModule() {
    napi_module_register(&calculatorModule);
}

Parameter Retrieval and Type Conversion

static napi_value ComputeSum(napi_env env, napi_callback_info info) {
    size_t expectedArgs = 2;
    size_t actualArgs = 2;
    napi_value parameters[2] = {nullptr};
    
    napi_get_cb_info(env, info, &actualArgs, parameters, nullptr, nullptr);

    napi_valuetype paramType1, paramType2;
    napi_typeof(env, parameters[0], &paramType1);
    napi_typeof(env, parameters[1], &paramType2);

    if (paramType1 != napi_number || paramType2 != napi_number) {
        napi_throw_type_error(env, nullptr, "Invalid arguments. Expected 2 numbers.");
        return nullptr;
    }

    double operand1, operand2;
    napi_get_value_double(env, parameters[0], &operand1);
    napi_get_value_double(env, parameters[1], &operand2);

    napi_value result;
    napi_create_double(env, operand1 + operand2, &result);
    
    return result;
}

Parameter Retrieval Function

The napi_get_cb_info function extracts parameter information from the callback context:

napi_status napi_get_cb_info(napi_env env,
                             napi_callback_info cbinfo,
                             size_t* argc,
                             napi_value* argv,
                             napi_value* this_arg,
                             void** data)
  • env: NAPI environment handlle
  • cbinfo: Callback information object
  • argc: Requested and actual parameter count
  • argv: Parameter array
  • this_arg: JavaScript 'this' object
  • data: Context data pointer

JavaScript to C++ Type Conversion

NAPI provides conversion functions for different data types:

  • napi_get_value_double
  • napi_get_value_int32
  • napi_get_value_uint32
  • napi_get_value_int64
  • napi_get_value_bool
  • napi_get_value_string_latin1
  • napi_get_value_string_utf8
  • napi_get_value_string_utf16

Type checking is performed using napi_typeof:

napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result)

C++ to JavaScript Type Conversion

Conversion from C++ types to JavaScript values uses creation functions:

  • napi_create_double
  • napi_create_int32
  • napi_create_uint32
  • napi_create_int64
  • napi_create_string_latin1
  • napi_create_string_utf8
  • napi_create_string_utf16

ArkUI Application Implementation

import calculator from '@ohos.calculator'

@Entry
@Component
export struct CalculatorApp {
  private inputController1: TextInputController = new TextInputController()
  private inputController2: TextInputController = new TextInputController()
  private title: string = 'C/C++ and JavaScript Data Type Conversion'
  private description: string = 'Calculate X + Y'
  private promptX: string = 'Enter X:'
  private promptY: string = 'Enter Y:'
  private resultLabel: string = 'Result:'
  private calculateButton: string = 'Calculate'
  
  @State result: number = 0.0
  @State valueX: number = 0.0
  @State valueY: number = 0.0

  build() {
    Row() {
      Column() {
        Row(){
          Text(this.title)
            .height('100%')
            .align(Alignment.Center)
            .fontSize(50)
            .fontWeight(800)
        }
        .height('30%')
        .width('100%')
        .justifyContent(FlexAlign.Center)

        Row(){
          Text(this.description)
            .height('100%')
            .align(Alignment.Center)
            .fontSize(35)
            .fontWeight(500)
        }
        .height('15%')
        .width('100%')
        .justifyContent(FlexAlign.Center)

        Row(){
          Text(this.promptX)
            .fontColor(Color.Black)
            .fontSize('65px')
            .width('30%')
            .height('100%')
            .margin({left:30})
          
          TextInput({ placeholder: 'X', controller: this.inputController1})
            .type(InputType.Number)
            .height('100%')
            .width('60%')
            .margin({left:10, right:30})
            .fontSize('25px')
            .onChange(value => { this.valueX = parseFloat(value) })
        }
        .height('6%')
        .width('100%')
        .justifyContent(FlexAlign.Start)

        Row(){
          Text(this.promptY)
            .fontColor(Color.Black)
            .fontSize('65px')
            .width('30%')
            .height('100%')
            .margin({left:30})
          
          TextInput({ placeholder: 'Y', controller: this.inputController2})
            .type(InputType.Number)
            .height('100%')
            .width('60%')
            .margin({left:10, right:30})
            .fontSize('25px')
            .onChange(value => { this.valueY = parseFloat(value) })
        }
        .height('6%')
        .width('100%')
        .margin({top:20})

        Row(){
          Text(this.resultLabel)
            .fontColor(Color.Black)
            .fontSize(35)
            .width('40%')
            .height('100%')
            .margin({left:30})
          
          Text('' + this.result)
            .fontColor(Color.Black)
            .fontSize(35)
            .width('60%')
            .height('100%')
        }
        .height('10%')
        .width('100%')
        .touchable(false)

        Row(){
          Button(this.calculateButton)
            .fontSize(37)
            .fontWeight(FontWeight.Bold)
            .margin({top:5})
            .height(80)
            .width(200)
            .onClick(() => {
              this.result = calculator.calculateSum(this.valueX, this.valueY)
            })
        }
        .height('30%')
        .width('100%')
        .justifyContent(FlexAlign.Center)
      }
      .width('100%')
    }
    .height('100%')
  }
}

TypeScript Interface Definition

declare namespace calculator {
  export const calculateSum: (x: number, y: number) => number;
}

export default calculator;

Tags: NAPI OpenHarmony Type Conversion C++ javascript

Posted on Mon, 14 Sep 2026 16:52:30 +0000 by wiggly81