npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jdes

v0.2.14

Published

A type safe JS runtime

Readme

Destructured JS

Build Status Coverage Status

A type safe JS runtime.

TODO for CLI

  • [x] target JS (cleanup)
  • [ ] target another PL that can export WASM or compile natively

API

Allow the definition of enums, structs, unions, or any other arbitrary type.

The type parameter can be either a string or an array of strings, in case of multiple types aliases.

The definition is either the returned value of enums, and struct, the union reference itself, or an object that exposes at least 2 methods: check(value, asArray) and cast(value).

define('special', {
  check(value, asArray) {
    // true when the type is in squared brackets
    return asArray ?
            value.map(v => this.check(v, false)) :
            value instanceof Special;
  },
  cast(value) {
    return this.check(value, false) ? value : new Special(value);
  }
});

const {special: single} = new Special;
const {[special]: multi} = [new Special, new Special];

It verifies that a specific value is an expected type, passing through the definition check(value, false) when type is not in square brackets, and check(value, true) when it is.

const value = 'test';
if (!is({string: value}))
  throw new TypeError(`unexpected ${value}`);

const values = ['a', 'b', 'c'];
if (!is({[string]: values}))
  throw new TypeError(`unexpected ${values}`);

It performs a cast through the definition cast(value) method, and it's responsibility of such method to understand what kind of value need to be casted, and throw in case there's no way to cast it.

// the following throws a TypeError
const {string: test} = 123;

// the following works as expected
const {string: test} = as({string: 123});

as({string: 123}) === "123"; // true

enums are simple, static, values that could be just named, or have a simple value.

enums are (currently?) defined in the global context, and it's not possible to define different enums with the same name.

define('Color', enums(
  'RED',      // by default enums are Symbol
  'GREEN',
  {BLUE: 123} // but these could be simple values too
));

console.log(Color);
// {RED: Symbol(RED), GREEN: Symbol(GREEN), BLUE: 123}

const {Color: red} = Color.RED;
const {[Color]: colors} = [Color.RED, Color.BLUE];

Differently from other types, enums cannot really be casted.

The syntax to define a safe function must provide all information needed to make it safe.

const sum = fn({int: ({int: arg0}, {int: arg1 = 0}) => {
  return arg0 + arg1;
}});

sum(1);         // 1
sum(1, 2);      // 3
sum('a', 'b');  // throws a TypeError

Please note:

  • optional arguments must be at the end of the signature. ({int: a}, {int: b = 1}) is OK, but ({int: a = 1}, {int: b}) is not.
  • the return type must always be present. If nothing is returned, a void type is expected
  • rest arguments are probably supported but these should not be used
  • for options/objects use the {object: {props}} if destructuring fields is needed
  • for overloads define unions

Differently from regular JS functions, jdes functions can be serialized as JSON, and these will be parsed back once parsed.

const json = JSON.serialize(sum);
const fn = JSON.parse(json);

fn(2, 3); // 5

In jdes classes are mostly discouraged for at least two reasons:

  • these cannot be used as type
  • these cannot target other programming languages, as they all have slightly different classes

Accordingly, whenever you think you need a class, you need to create a struct.

define('Point2D', struct(
  // mandatory fields {type: name}
  {int: 'x'},
  {int: 'y'}
));

// literals are casted automatically
const {Point2D: p2d} = {x: 1, y: 2};
const {[Point2D]: p2ds} = [p2d, {x: 3, y: 2}];

// also OK through explicit new Point2D
const myPoint = new Point2D({x: 1, y: 2});

If a mandatory field is not available as literal property, a TypeError will be thrown.

However, fields can also have optional entries that don't need to be present in the literal.

define('Point3D', struct(
  // mandatory fields
  // multiple type: [name, ...] allowed
  {int: ['x', 'y']},
  // optional fields
  // {type: {name: defaultValue}}
  {int: {z: 0}}
));

const {Point3D: p3d} = {x: 1, y: 2};
p3d.z; // 0

A struct can also have methods, which are just guarded functions.

define('Point3D', struct(
  {int: ['x', 'y']},  // mandatory fields
  {int: {z: 0}},      // default fields
  // methods {returnType: {methodName({argType: name}, ...) {}}}
  {[int]: {coords() {
    return [this.x, this.y, this.z];
  }}}
));
const {Point3D: p3d} = {x: 1, y: 2};
p3d.coords();       // [1, 2, 0]

The union utility makes overloads possible by defining multiple known types separated by an underscore.

define('int_float', union);
const {int_float: a} = 1;
const {int_float: b} = 1.2;
const {[int_float]: c} = [a, b];

As the _ underscore is used to split/check types, it is a good idea to never define a type within an underscore, in case it needs to be used as union.

The map utility helps defining Map instances with a well known type for both keys or values.

define('StrInt', map(str, int));

// maps definition work via shortcut
const {StrInt: si} = [];
si.set('one', 1); // OK
si.set(1, 'one'); // fails

Please note, when targeting compilable targets it is mandatory to define typed maps, and jdes maps should not be iterated right away:

// this breaks
for (const [key, value] of si);

// this works
for (const [key, value] of si.entries());

This is due to the fact for/of loops in jdes are always transformed as regular array loops.

The set utility helps defining Set instances with a well known type for values.

define('Str', set(str));

// maps definition work via shortcut
const {Str: s} = [];
s.add('one'); // OK
s.add(1);     // fails

Please note, when targeting compilable targets it is mandatory to define typed sets, and jdes sets should not be iterated right away:

// this breaks
for (const value of s);

// this works
for (const value of s.values());

This is due to the fact for/of loops in jdes are always transformed as regular array loops.

jdes runtime guards properties access, type checks, arguments and much more, but all these runtime checks come with a cost.

Even if performance are still very reasonable, a safe execution takes 10X up to 1000X what would be an unsafe execution time.

Accordingly, it is highly recommended to mark jdes unsafe after importing it, when the code is meant to run in production.

import {unsafe} from 'jdes';
if (global.PRODUCTION)
  unsafe();

The unsafe call is not reversible: once jdes is unsafe it's unsafe.

If the code is transpiled though, and JS is used as target, there's no need to flag anything unsafe, as the environment will be super clean and no guards whatsoever are used.

These are all the pre-defined generic types:

  • int a generic integer, casted via parseInt(value, 10)
  • float a generic float, casted via parseFloat(value)
  • boolean - bool either true or false, casted via Boolean(value)
  • number - num a generic number, casted via Number(value)
  • string - str a generic string, casted via String(value)
  • object - obj a generic object (literal/instance), casted via Object(value)
  • function - fn a generic function, casted via Function when parsed via JSON
  • void usable to describe functions return type

There is no array type for the simple reason that any type, except for the void one, can be part of an array.

// not an array
const {int: i} = 0;
// as array of int
const {[int]: ii} = [0, 0];

These are all specialized types:

  • f32 a Float32Array compatible number
  • f64 - double a Float64Array compatible number
  • i8 an Int8Array compatible number
  • i16 an Int16Array compatible number
  • i32 an Int32Array compatible number
  • u8 a Uint8Array compatible number
  • u16 a Uint16Array compatible number
  • u32 a Uint32Array compatible number
  • uc8 a Uint8ClampedArray compatible number
  • i64 a BigInt64Array compatible number
  • u64 a BigUint64Array compatible number

Each specialized type cast, as value, is performed by setting the value within the index 0 and retrieving it back, while as array, the cast is done via new SpecialConstructor(array) if the array is not already an instanceof such constructor.

Please note that all specialized types are static when retrieved as array.

// single value
const {i32: i} = 0;

// as array - implicit cast
const {[i32]: ii} = [1, 2, 3];

If a predefined length is needed, it is always possible to create values explicitly.

const ii = new Int32Array(100);

Please note that not all these types are necessarily available, as some engine might not have all of them.

Getting Started

Codepen playground

import {
  define,                   // used to define types
  as, is,                   // cast and check utils
  enums, fn, struct, union, // specialized types
  set, map,                 // typed Set and Map
  unsafe                    // performance boost for production
} from 'jdes';

// values and arrays declaration
const {int: i} = 0;                     // is({int: i});
const {[int]: ii} = [1, 2];             // is({[int]: ii});

// explicit cast
const {string: s} = as({string: 123});  // s === "123"

// unions (multi type overloads)
define('int_float', union);

// functions {returnType: ({argType: name}, ...) => {}}
const squared = fn({int: ({int_float: num = 0}) => num * num});
squared(3); // 9

// enums
define('RGB', enums('RED', 'GREEN', 'BLUE'));
const {RGB: color} = RGB.GREEN;

// struct
define('Point3D', struct(
  {int: ['x', 'y']},  // mandatory properties
  {int: {z: 0}},      // default properties
  // methods {returnType: {methodName({argType: name}, ...) {}}}
  {[int]: {coords() {
    return [this.x, this.y, this.z];
  }}}
));
const {Point3D: p3d} = {x: 1, y: 2};
p3d.coords();       // [1, 2, 0]
is({Point3D: p3d}); // true

// set
define('Str', set(str));
const {Str: typedSet} = ['entry'];

// map
define('StrInt', map(str, int));
const {StrInt: typedMap} = [['first', 1]];