Last active
May 30, 2025 08:08
-
-
Save nirlanka/e2a1b9a590cef0f13ffee8969f47c455 to your computer and use it in GitHub Desktop.
Pure Javascript Asserts and Type System
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Pure Javascript Asserts and Type System</title> | |
</head> | |
<body style="background-color: #222; color: #fff;"> | |
<h1>Test assert everywhere</h1> | |
<script> | |
window.__libs__ = {}; | |
</script> | |
<script> | |
{ | |
function assert( | |
/** @type {() => boolean} */ testFn, | |
/** @type {string} */ textOnFail, | |
/** @type {Array|Object} */ values, | |
) { | |
try { | |
const isTestPass = testFn(); | |
if (!isTestPass) | |
throw Error(`Assert failure: NOT ${textOnFail}, with debug values: ${values ? JSON.stringify(values) : testFn}`); | |
} catch (err) { | |
console.error('[ASSERT FAILURE]', err); | |
} | |
} | |
window.__libs__.assert = assert; | |
} | |
</script> | |
<script> | |
{ | |
const { assert } = window.__libs__; | |
for (const value of [ | |
'', | |
"test", | |
12, | |
'true', | |
]) { | |
assert( | |
() => ['true', 'false'].some(x => x === value), | |
"a proper string version of the boolean."); | |
} | |
} | |
</script> | |
<script> | |
{ | |
const { assert } = window.__libs__; | |
function assertType(instance, type) { | |
assert( | |
() => instance.constructor.name === type.name, | |
"the same type as expected [TypeBase]", | |
[instance, type] | |
); | |
} | |
class TBase { | |
static _PRIMITIVES = [ | |
'number', | |
'string', | |
'object', | |
]; | |
/** | |
* @abstract | |
* @type {string} | |
* Underlying primitive data type | |
*/ | |
primitive; | |
_validate() { | |
assert( | |
() => TBase._PRIMITIVES.some(x => x === this.primitive), | |
"a valid primitive type name [TypeBase]", | |
[this.primitive] | |
); | |
assert( | |
() => (this._value !== undefined) | |
? (typeof this._value === this.primitive) | |
: true, | |
"a valid primitive type value [TypeBase]", | |
[this._value, this.primitive] | |
); | |
if (this.primitive === 'object') { | |
assert( | |
() => | |
(typeof this.types === 'object') | |
&& Object.keys(this.types).every(k => typeof k === 'string') | |
&& Object.values(this.types).every(v => typeof v === 'function'), | |
"a valid typed object", | |
[this.types, this.primitive] | |
); | |
for (const k in this.types) { | |
assertType(this._value[k], this.types[k]); | |
} | |
} | |
assert( | |
() => this.assert(this._value), | |
"a valid value [TypeBase]", | |
[this._value], | |
); | |
} | |
/** | |
* @type {any} | |
*/ | |
_value; | |
/** @final */ | |
set(/** @type {any} */ v) { | |
this._value = v; | |
this._validate(); | |
return this; | |
} | |
/** | |
* @final | |
* @returns {any} reference to the stored primitive value | |
*/ | |
get() { | |
this._validate(); | |
return this._value; | |
} | |
/** | |
* @abstract | |
* @returns {boolean} | |
*/ | |
assert(/** @type {any} */ value) { | |
return true; | |
} | |
} | |
window.__libs__.TBase = TBase; | |
window.__libs__.assertType = assertType; | |
} | |
</script> | |
<script> | |
{ | |
const { TBase, assertType } = window.__libs__; | |
class TNaturalNumber extends TBase { | |
primitive = 'number'; | |
assert(value) { | |
return value >= 0; | |
} | |
} | |
/** @type {TNaturalNumber} */ | |
const x1 = new TNaturalNumber().set('9'); | |
console.log(x1); | |
/** @type {TNaturalNumber} */ | |
const x2 = new TNaturalNumber().set(13); | |
console.log(x2); | |
/** @type {TNaturalNumber} */ | |
const x3 = new TNaturalNumber().set(-4); | |
console.log(x3); | |
class TUnformattedText extends TBase { | |
primitive = 'string'; | |
assert(value) { | |
return !!value; | |
} | |
} | |
/** Radio component option value **/ | |
class TRadioOption extends TBase { | |
primitive = 'object'; | |
types = { | |
'value': TNaturalNumber, | |
'label': TUnformattedText | |
}; | |
} | |
/** @type {TRadioOption} */ | |
const x4 = new TRadioOption().set({ | |
value: new TNaturalNumber().set(0), | |
label: new TUnformattedText().set("Option 0"), | |
}); | |
console.log(x4); | |
console.log(x4.get().value); | |
console.log(x4.get().value.get()); | |
/** @type {TRadioOption} */ | |
const x5 = new TRadioOption().set({ | |
value: new TNaturalNumber().set(-1), | |
label: new TUnformattedText().set("[Please select]"), | |
}); | |
console.log(x5); | |
console.log(x5.get().value); | |
console.log(x5.get().value.get()); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment