Defining class

Real world example

This example creates a class named TestClass which has a JavaScript property named count and wraps a rust struct NativeClass on the instance of the object which has a property value. The methods can access both the object’s properties and the wrapped struct.

lib.rs
#[macro_use]
extern crate napi_derive;
 
use std::convert::TryInto;
use napi::{CallContext, JsNumber, JsObject, JsUndefined, Property, Result, Env};
 
struct NativeClass {
  value: i32,
}
 
#[js_function(1)]
fn test_class_constructor(ctx: CallContext) -> Result<JsUndefined> {
  let count: i32 = ctx.get::<JsNumber>(0)?.try_into()?;
  let mut this: JsObject = ctx.this_unchecked();
  ctx
    .env
    .wrap(&mut this, NativeClass { value: count + 100 })?;
  this.set_named_property("count", ctx.env.create_int32(count)?)?;
  ctx.env.get_undefined()
}
 
#[js_function(1)]
fn add_count(ctx: CallContext) -> Result<JsNumber> {
  let add: i32 = ctx.get::<JsNumber>(0)?.try_into()?;
  let mut this: JsObject = ctx.this_unchecked();
  let count: i32 = this.get_named_property::<JsNumber>("count")?.try_into()?;
  this.set_named_property("count", ctx.env.create_int32(count + add)?)?;
  this.get_named_property("count")
}
 
#[js_function(1)]
fn add_native_count(ctx: CallContext) -> Result<JsNumber> {
  let add: i32 = ctx.get::<JsNumber>(0)?.try_into()?;
  let this: JsObject = ctx.this_unchecked();
  let native_class: &mut NativeClass = ctx.env.unwrap(&this)?;
  native_class.value += add;
  ctx.env.create_int32(native_class.value)
}
 
#[module_exports]
pub fn init(mut exports: JsObject, env: Env) -> Result<()> {
  let test_class = env
    .define_class("TestClass", test_class_constructor, &[
      Property::new(&env, "addCount")?.with_method(add_count),
      Property::new(&env, "addNativeCount")?.with_method(add_native_count),
    ])?;
  exports.set_named_property("TestClass", test_class)?;
 
  Ok(())
}
const { TestClass } = require('./index.node')
 
const test = new TestClass(100)
 
test.addCount()