Meta types - chung-leong/zigar GitHub Wiki

Certain Zig types can be represented in JavaScript in multiple ways. The best example is []const u8. Very often the type is used to store a text string. At times it's used to store opaque binary data, in which case a Uint8Array would make better sense. If the u8s are pixel values, then perhaps a Uint8ClampedArray is better, since that's what the browser's ImageData expects. Finally, the []const u8 could simply be a series of positive numbers that happen to never exceed 255.

By default, Zigar gives you a Zig data object. Through its speical properties and methods, you can obtain the right form for a given situation:

slice.string => "Hello"
slice.typeArray => Uint8Array(5) [ 104, 101, 108, 108, 111 ]
slice.clampedArray => Uint8ClampedArray(5) [ 104, 101, 108, 108, 111 ]
slice.valueOf() => [ 104, 101, 108, 108, 111 ]

To make the transition from Zig to JS more seamless, you can tell Zigar to automatically perform the above operation for you using its meta-type mechanism. It's based on functions declared in a namespace under a special name:

const std = @import("std");

pub const string: []const u8 = "Hello";
pub const typedArray = string;
pub const clampedArray = string;
pub const plain = string;

const module = @This();
pub const @"meta(zigar)" = struct {
    pub fn isDeclString(comptime T: type, comptime name: std.meta.DeclEnum(T)) bool {
        return T == module and name == .string;
    }

    pub fn isDeclClampedArray(comptime T: type, comptime name: std.meta.DeclEnum(T)) bool {
        return T == module and name == .clampedArray;
    }

    pub fn isDeclTypedArray(comptime T: type, comptime name: std.meta.DeclEnum(T)) bool {
        return T == module and name == .typedArray;
    }

    pub fn isDeclPlain(comptime T: type, comptime name: std.meta.DeclEnum(T)) bool {
        return T == module and name == .plain;
    }
};
import { clampedArray, plain, string, typedArray } from './meta-type-example-1.zig';

console.log({ string, clampedArray, typedArray, plain });
{
  string: 'Hello',
  clampedArray: Uint8ClampedArray(5) [ 72, 101, 108, 108, 111 ],
  typedArray: Uint8Array(5) [ 72, 101, 108, 108, 111 ],
  plain: [ 72, 101, 108, 108, 111 ]
}

If you're unfamiliar with Zig's @"some name" syntax, all it does is let you use non-alphanumeric characters in an identifier.

@"meta(zigar) must be in the root namespace. Its functions can impact on all namespaces in-use, including those in std.

During the export process, these functions are called in the same order as they're listed in the example above and below. The first to return true determines the meta type:

const std = @import("std");

pub const string: []const u8 = "Hello";
pub const typedArray: []const f32 = &.{ 1, 2, 3, 4 };

const module = @This();
pub const @"meta(zigar)" = struct {
    pub fn isDeclString(comptime T: type, comptime _: std.meta.DeclEnum(T)) bool {
        return true;
    }

    pub fn isDeclClampedArray(comptime T: type, comptime _: std.meta.DeclEnum(T)) bool {
        return true;
    }

    pub fn isDeclTypedArray(comptime T: type, comptime _: std.meta.DeclEnum(T)) bool {
        return true;
    }

    pub fn isDeclPlain(comptime T: type, comptime _: std.meta.DeclEnum(T)) bool {
        return true;
    }
};
import { string, typedArray } from './meta-type-example-2.zig';

console.log({ string, typedArray });
{ string: 'Hello', typedArray: Float32Array(4) [ 1, 2, 3, 4 ] }

The isFieldXXX and isArgumentXXX sets of functions operate in an analogous manner for assigning meta-type to fields and callback arguments.


Text String | Clamped array | Typed array | Plain object