# jdes
**Repository Path**: mirrors_WebReflection/jdes
## Basic Information
- **Project Name**: jdes
- **Description**: A type safe JS runtime
- **Primary Language**: Unknown
- **License**: ISC
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-09-16
- **Last Updated**: 2025-12-27
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Destructured JS
[](https://travis-ci.com/WebReflection/jdes) [](https://coveralls.io/github/WebReflection/jdes?branch=master)
A type safe JS runtime.
### TODO for CLI
- [x] target JS (cleanup)
- [ ] target another PL that can export WASM or compile natively
## API
define(type, definition)
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)`.
```js
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];
```
is({type: value})
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.
```js
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}`);
```
as({type: value})
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.
```js
// 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(name, ...)
_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.
```js
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.
fn({returnType: (...) => {}})
The syntax to define a safe function must provide all information needed to make it safe.
```js
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.
```js
const json = JSON.serialize(sum);
const fn = JSON.parse(json);
fn(2, 3); // 5
```
struct(field, method, ...)
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_.
```js
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.
```js
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.
```js
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]
```
union
The _union_ utility makes overloads possible by defining multiple known types separated by an underscore.
```js
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.
map
The _map_ utility helps defining *Map* instances with a well known type for both keys or values.
```js
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:
```js
// 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.
set
The _set_ utility helps defining *Set* instances with a well known type for values.
```js
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:
```js
// 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.
unsafe()
_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.
```js
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.
types
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.
```js
// 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*.
```js
// 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.
```js
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](https://codepen.io/WebReflection/pen/abNjjoV?editors=0011)
```js
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]];
```