{"id":20514123,"url":"https://github.com/webreflection/jdes","last_synced_at":"2025-04-14T00:10:58.894Z","repository":{"id":65993202,"uuid":"295694361","full_name":"WebReflection/jdes","owner":"WebReflection","description":"A type safe JS runtime","archived":false,"fork":false,"pushed_at":"2020-10-04T16:50:38.000Z","size":3022,"stargazers_count":19,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T14:12:34.878Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WebReflection.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-09-15T10:37:02.000Z","updated_at":"2022-03-06T03:48:47.000Z","dependencies_parsed_at":"2023-05-21T17:00:36.903Z","dependency_job_id":null,"html_url":"https://github.com/WebReflection/jdes","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fjdes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fjdes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fjdes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebReflection%2Fjdes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WebReflection","download_url":"https://codeload.github.com/WebReflection/jdes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248799955,"owners_count":21163404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T21:14:52.308Z","updated_at":"2025-04-14T00:10:58.858Z","avatar_url":"https://github.com/WebReflection.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Destructured JS\n\n[![Build Status](https://travis-ci.com/WebReflection/jdes.svg?branch=master)](https://travis-ci.com/WebReflection/jdes) [![Coverage Status](https://coveralls.io/repos/github/WebReflection/jdes/badge.svg?branch=master)](https://coveralls.io/github/WebReflection/jdes?branch=master)\n\nA type safe JS runtime.\n\n### TODO for CLI\n- [x] target JS (cleanup)\n- [ ] target another PL that can export WASM or compile natively\n\n## API\n\n\u003cdetails id=\"api-define\"\u003e\n  \u003csummary\u003e\u003cstrong\u003edefine(type, definition)\u003c/strong\u003e\u003c/summary\u003e\n\nAllow the definition of enums, structs, unions, or any other arbitrary type.\n\nThe `type` parameter can be either a _string_ or an _array of strings_, in case of multiple types aliases.\n\nThe `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)`.\n\n```js\ndefine('special', {\n  check(value, asArray) {\n    // true when the type is in squared brackets\n    return asArray ?\n            value.map(v =\u003e this.check(v, false)) :\n            value instanceof Special;\n  },\n  cast(value) {\n    return this.check(value, false) ? value : new Special(value);\n  }\n});\n\nconst {special: single} = new Special;\nconst {[special]: multi} = [new Special, new Special];\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails id=\"api-is\"\u003e\n  \u003csummary\u003e\u003cstrong\u003eis({type: value})\u003c/strong\u003e\u003c/summary\u003e\n\nIt 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.\n\n```js\nconst value = 'test';\nif (!is({string: value}))\n  throw new TypeError(`unexpected ${value}`);\n\nconst values = ['a', 'b', 'c'];\nif (!is({[string]: values}))\n  throw new TypeError(`unexpected ${values}`);\n```\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-as\"\u003e\n  \u003csummary\u003e\u003cstrong\u003eas({type: value})\u003c/strong\u003e\u003c/summary\u003e\n\nIt 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.\n\n```js\n// the following throws a TypeError\nconst {string: test} = 123;\n\n// the following works as expected\nconst {string: test} = as({string: 123});\n\nas({string: 123}) === \"123\"; // true\n```\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-enums\"\u003e\n  \u003csummary\u003e\u003cstrong\u003eenums(name, ...)\u003c/strong\u003e\u003c/summary\u003e\n\n_enums_ are simple, static, values that could be just named, or have a simple value.\n\n_enums_ are (currently?) defined in the global context, and it's not possible to define different enums with the same name.\n\n```js\ndefine('Color', enums(\n  'RED',      // by default enums are Symbol\n  'GREEN',\n  {BLUE: 123} // but these could be simple values too\n));\n\nconsole.log(Color);\n// {RED: Symbol(RED), GREEN: Symbol(GREEN), BLUE: 123}\n\nconst {Color: red} = Color.RED;\nconst {[Color]: colors} = [Color.RED, Color.BLUE];\n```\n\nDifferently from other types, _enums_ cannot really be casted.\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-fn\"\u003e\n  \u003csummary\u003e\u003cstrong\u003efn({returnType: (...) =\u003e {}})\u003c/strong\u003e\u003c/summary\u003e\n\nThe syntax to define a safe function must provide all information needed to make it safe.\n\n```js\nconst sum = fn({int: ({int: arg0}, {int: arg1 = 0}) =\u003e {\n  return arg0 + arg1;\n}});\n\nsum(1);         // 1\nsum(1, 2);      // 3\nsum('a', 'b');  // throws a TypeError\n```\n\nPlease note:\n\n  * 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.\n  * the return type must always be present. If nothing is returned, a `void` type is expected\n  * _rest_ arguments are probably supported but these should *not* be used\n  * for options/objects use the `{object: {props}}` if destructuring fields is needed\n  * for overloads define *unions*\n\nDifferently from regular JS functions, _jdes_ functions can be serialized as _JSON_, and these will be parsed back once parsed.\n\n```js\nconst json = JSON.serialize(sum);\nconst fn = JSON.parse(json);\n\nfn(2, 3); // 5\n```\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-struct\"\u003e\n  \u003csummary\u003e\u003cstrong\u003estruct(field, method, ...)\u003c/strong\u003e\u003c/summary\u003e\n\nIn *jdes* classes are mostly discouraged for at least two reasons:\n\n  * these cannot be used as _type_\n  * these cannot target other programming languages, as they all have slightly different classes\n\nAccordingly, whenever you think you need a _class_, you need to create a _struct_.\n\n```js\ndefine('Point2D', struct(\n  // mandatory fields {type: name}\n  {int: 'x'},\n  {int: 'y'}\n));\n\n// literals are casted automatically\nconst {Point2D: p2d} = {x: 1, y: 2};\nconst {[Point2D]: p2ds} = [p2d, {x: 3, y: 2}];\n\n// also OK through explicit new Point2D\nconst myPoint = new Point2D({x: 1, y: 2});\n```\n\nIf a mandatory field is not available as literal property, a _TypeError_ will be thrown.\n\nHowever, fields can also have **optional** entries that don't need to be present in the literal.\n\n```js\ndefine('Point3D', struct(\n  // mandatory fields\n  // multiple type: [name, ...] allowed\n  {int: ['x', 'y']},\n  // optional fields\n  // {type: {name: defaultValue}}\n  {int: {z: 0}}\n));\n\nconst {Point3D: p3d} = {x: 1, y: 2};\np3d.z; // 0\n```\n\nA _struct_ can also have **methods**, which are just guarded functions.\n\n```js\ndefine('Point3D', struct(\n  {int: ['x', 'y']},  // mandatory fields\n  {int: {z: 0}},      // default fields\n  // methods {returnType: {methodName({argType: name}, ...) {}}}\n  {[int]: {coords() {\n    return [this.x, this.y, this.z];\n  }}}\n));\nconst {Point3D: p3d} = {x: 1, y: 2};\np3d.coords();       // [1, 2, 0]\n```\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-union\"\u003e\n  \u003csummary\u003e\u003cstrong\u003eunion\u003c/strong\u003e\u003c/summary\u003e\n\nThe _union_ utility makes overloads possible by defining multiple known types separated by an underscore.\n\n```js\ndefine('int_float', union);\nconst {int_float: a} = 1;\nconst {int_float: b} = 1.2;\nconst {[int_float]: c} = [a, b];\n```\n\nAs 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.\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-map\"\u003e\n  \u003csummary\u003e\u003cstrong\u003emap\u003c/strong\u003e\u003c/summary\u003e\n\nThe _map_ utility helps defining *Map* instances with a well known type for both keys or values.\n\n```js\ndefine('StrInt', map(str, int));\n\n// maps definition work via shortcut\nconst {StrInt: si} = [];\nsi.set('one', 1); // OK\nsi.set(1, 'one'); // fails\n```\n\nPlease note, when targeting compilable targets it is *mandatory* to define typed maps, and *jdes* maps should not be iterated right away:\n\n```js\n// this breaks\nfor (const [key, value] of si);\n\n// this works\nfor (const [key, value] of si.entries());\n```\n\nThis is due to the fact `for/of` loops in *jdes* are always transformed as regular array loops.\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-set\"\u003e\n  \u003csummary\u003e\u003cstrong\u003eset\u003c/strong\u003e\u003c/summary\u003e\n\nThe _set_ utility helps defining *Set* instances with a well known type for values.\n\n```js\ndefine('Str', set(str));\n\n// maps definition work via shortcut\nconst {Str: s} = [];\ns.add('one'); // OK\ns.add(1);     // fails\n```\n\nPlease note, when targeting compilable targets it is *mandatory* to define typed sets, and *jdes* sets should not be iterated right away:\n\n```js\n// this breaks\nfor (const value of s);\n\n// this works\nfor (const value of s.values());\n```\n\nThis is due to the fact `for/of` loops in *jdes* are always transformed as regular array loops.\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-unsafe\"\u003e\n  \u003csummary\u003e\u003cstrong\u003eunsafe()\u003c/strong\u003e\u003c/summary\u003e\n\n_jdes_ runtime guards properties access, type checks, arguments and much more, but all these runtime checks come with a cost.\n\nEven if performance are still very reasonable, a safe execution takes 10X up to 1000X what would be an _unsafe_ execution time.\n\nAccordingly, it is highly recommended to mark _jdes_ unsafe after importing it, when the code is meant to run in production.\n\n```js\nimport {unsafe} from 'jdes';\nif (global.PRODUCTION)\n  unsafe();\n```\n\nThe `unsafe` call is not reversible: once _jdes_ is unsafe it's unsafe.\n\nIf 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.\n\n\u003c/details\u003e\n\n\u003cdetails id=\"api-types\"\u003e\n  \u003csummary\u003e\u003cstrong\u003etypes\u003c/strong\u003e\u003c/summary\u003e\n\nThese are all the pre-defined **generic** types:\n\n  * `int` a generic integer, casted via `parseInt(value, 10)`\n  * `float` a generic float, casted via `parseFloat(value)`\n  * `boolean` - `bool` either `true` or `false`, casted via `Boolean(value)`\n  * `number` - `num` a generic number, casted via `Number(value)`\n  * `string` - `str` a generic string, casted via `String(value)`\n  * `object` - `obj` a generic object (literal/instance), casted via `Object(value)`\n  * `function` - `fn` a generic function, casted via `Function` when parsed via _JSON_\n  * `void` usable to describe _functions_ return type\n\nThere is no `array` type for the simple reason that any type, except for the `void` one, can be part of an array.\n\n```js\n// not an array\nconst {int: i} = 0;\n// as array of int\nconst {[int]: ii} = [0, 0];\n```\n\nThese are all **specialized** types:\n\n  * `f32` a *Float32Array* compatible number\n  * `f64` - `double` a *Float64Array* compatible number\n  * `i8` an *Int8Array* compatible number\n  * `i16` an *Int16Array* compatible number\n  * `i32` an *Int32Array* compatible number\n  * `u8` a *Uint8Array* compatible number\n  * `u16` a *Uint16Array* compatible number\n  * `u32` a *Uint32Array* compatible number\n  * `uc8` a *Uint8ClampedArray* compatible number\n  * `i64` a *BigInt64Array* compatible number\n  * `u64` a *BigUint64Array* compatible number\n\nEach 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.\n\nPlease note that all specialized types are *static* when retrieved as *array*.\n\n```js\n// single value\nconst {i32: i} = 0;\n\n// as array - implicit cast\nconst {[i32]: ii} = [1, 2, 3];\n```\n\nIf a predefined length is needed, it is always possible to create values explicitly.\n\n```js\nconst ii = new Int32Array(100);\n```\n\nPlease note that not all these types are necessarily available, as some engine might not have all of them.\n\n\u003c/details\u003e\n\n\n\n### Getting Started\n\n[Codepen playground](https://codepen.io/WebReflection/pen/abNjjoV?editors=0011)\n\n```js\nimport {\n  define,                   // used to define types\n  as, is,                   // cast and check utils\n  enums, fn, struct, union, // specialized types\n  set, map,                 // typed Set and Map\n  unsafe                    // performance boost for production\n} from 'jdes';\n\n// values and arrays declaration\nconst {int: i} = 0;                     // is({int: i});\nconst {[int]: ii} = [1, 2];             // is({[int]: ii});\n\n// explicit cast\nconst {string: s} = as({string: 123});  // s === \"123\"\n\n// unions (multi type overloads)\ndefine('int_float', union);\n\n// functions {returnType: ({argType: name}, ...) =\u003e {}}\nconst squared = fn({int: ({int_float: num = 0}) =\u003e num * num});\nsquared(3); // 9\n\n// enums\ndefine('RGB', enums('RED', 'GREEN', 'BLUE'));\nconst {RGB: color} = RGB.GREEN;\n\n// struct\ndefine('Point3D', struct(\n  {int: ['x', 'y']},  // mandatory properties\n  {int: {z: 0}},      // default properties\n  // methods {returnType: {methodName({argType: name}, ...) {}}}\n  {[int]: {coords() {\n    return [this.x, this.y, this.z];\n  }}}\n));\nconst {Point3D: p3d} = {x: 1, y: 2};\np3d.coords();       // [1, 2, 0]\nis({Point3D: p3d}); // true\n\n// set\ndefine('Str', set(str));\nconst {Str: typedSet} = ['entry'];\n\n// map\ndefine('StrInt', map(str, int));\nconst {StrInt: typedMap} = [['first', 1]];\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebreflection%2Fjdes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebreflection%2Fjdes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebreflection%2Fjdes/lists"}