feat(dsl): add constructor to js.Object(T) for one-line object creation

Open Beginner friendly
#44 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
76/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Quiet
Tech stack
zig
Domain
api

Research direction

Start in src/js/object.zig and inspect the type returned by Object(T), its set method, and how String.from or Number.from access the environment. Add the proposed constructor using the existing object-creation and population paths, while keeping get, set, and toValue unchanged. Done means callers can create and populate a typed JS object in one call.

Written by the indexing model from the issue text.

Description

Summary

Add a constructor (init / from) to js.Object(T) so user code can create and populate a JS object in a single call, instead of manually creating a raw object, wrapping it, and calling set.

Motivation

Defining an object shape is ergonomic:

pub const BitArray = js.Object(struct {
    uint8Array: js.Uint8Array,
    bitLen: js.Number,
});

But creating an instance at runtime currently requires the low-level dance:

const e = js.env();
const raw = try e.createObject();   // napi.Value, a fresh {} object
var obj = BitArray{ .val = raw };   // wrap it
try obj.set(.{ .uint8Array = ..., .bitLen = ... });

Object(T) (src/js/object.zig) currently exposes only validateArg, get, set, and toValue — there is no constructor. This boilerplate is repetitive and leaks N-API details into otherwise high-level DSL code.

Proposed solution

Add an init method to the type returned by Object(T). It already knows T and has set, so it just needs to create the underlying object and populate it:

/// Creates a new JS object and populates it from the Zig struct `T`.
pub fn init(value: T) !Self {
    const self = Self{ .val = try env().createObject() };
    try self.set(value);
    return self;
}

(using the in-scope env accessor, matching how String.from / Number.from reach the env)

Call site collapses to:

const ba = try BitArray.init(.{
    .uint8Array = js.Uint8Array.from(&bytes),
    .bitLen = js.Number.from(@as(i32, 42)),
});

Naming

The scalar DSL wrappers already use from(...) (String.from, Number.from, Boolean.from, Date.from, Uint8Array.from). For consistency we could name it from instead of init — open to either. init reads slightly better here since the argument is a struct of fields rather than a single scalar.

Notes / open questions

  • Should there also be an empty constructor (e.g. empty() / init(.{}) when all fields are optional) for incremental population? Probably out of scope for the first pass.
  • Keep the existing set/get/toValue API unchanged; this is purely additive.
Dominant language
Zig
Stars
4
Forks
4
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from ChainSafe/zapi

All issues in ChainSafe/zapi

Similar issues

More Backend & API Design issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.