From 4e54af5577dc36500b120968a17854cef1b79bfe Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Sat, 17 Feb 2024 11:04:57 -0500 Subject: [PATCH 01/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7ea79c..5a976b5 100644 --- a/README.md +++ b/README.md @@ -130,5 +130,5 @@ Note that JavaScript doesn't always treat mathematical expressions of undefined | Method | Description | - | - -| is.**empty**(_value_) | Tests if an object's `.length` or `.size` property equals zero. +| is.**empty**(_value_) | Tests for an empty _string_ primitive or if an object's `.length` or `.size` property equals zero. | is.**of**(_value_, _class_) | Tests if _value_ is an instance of _class_. (Same as using the `instanceof` operator.) From 7417964ac9ba499829af23b49fde6f9f88a5ad66 Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Tue, 31 Dec 2024 03:47:21 -0500 Subject: [PATCH 02/14] rewrite; test and docs WIP --- isType.mjs.js | 219 ++++++++++++++++++-------------------------------- 1 file changed, 77 insertions(+), 142 deletions(-) diff --git a/isType.mjs.js b/isType.mjs.js index cc6ba78..d752d3c 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -1,164 +1,99 @@ /** @module isType */ -// Type names and the class each refers to. -const OBJECT_TYPES = { - array: Array, - boolean: Boolean, - date: Date, - error: Error, - function: Function, - map: Map, - number: Number, - promise: Promise, - regex: RegExp, - set: Set, - string: String, - weakmap: WeakMap, - weakset: WeakSet, +const TYPES = { + array: { class: Array }, + bigint: { primitive: "bigint" }, + boolean: { primitive: "boolean", class: Boolean }, + date: { class: Date }, + error: { class: Error }, + function: { class: Function }, + map: { class: Map }, + nan: {}, + null: {}, + number: {}, + numberish: { primitive: "number", class: Number }, + object: {}, + promise: { class: Promise }, + regex: { class: RegExp }, + set: { class: Set }, + string: { primitive: "string", class: String }, + symbol: { primitive: "symbol", }, + undefined: { primitive: "undefined", }, + weakmap: { class: WeakMap }, + weakset: { class: WeakSet }, }; -// Initial type names. -const TYPE_NAMES = [ - "array", - "bigint", - "boolean", - "date", - "error", - "function", - "map", - "nan", - "null", - "number", - "object", - "promise", - "regex", - "set", - "string", - "symbol", - "undefined", - "weakmap", - "weakset", -]; - - /** - * Descriptors of a value's type. - * @class Type + * A collection of boolean properties indicating the type of the given value. + * @class TypeTest * @extends {String} */ -class Type extends String { - - #name; - #typeofType; - #toStringTag; - #constructorName; - #isPrimitive; +class TypeTest extends String { /** * @constructor - * @param {string} name - Custom type name. - * @param {string} typeofType - The value returned by the `typeof` operator. - * @param {string} [toStringTag] - For objects. The name used by `Object.prototype.toString.call()`. - * @param {string} [constructorName] - For objects. The name of the argument's constructor. + * @param {*} value - The value to be tested. */ - constructor(name, typeofType, toStringTag, constructorName){ + constructor(value){ - super(name); - this.#name = name; + let typeName; - this.#typeofType = typeofType; - this.#toStringTag = toStringTag; - this.#constructorName = constructorName; + if(value === null){ + typeName = "null"; + } + else{ + typeName = "object"; + + for(const name in TYPES){ + const type = TYPES[name]; + if(typeof value === type.primitive || (type.class && value instanceof type.class)){ + typeName = name; + break; + } + } + } - this.#isPrimitive = constructorName === void 0; - } - - /** @member {string} */ - get type(){ return this.#name; } - - /** @member {string} */ - get ["typeof"](){ return this.#typeofType; } - - /** @member {string} */ - get toStringTag(){ return this.#toStringTag; } - - /** @member {string} */ - get constructorName(){ return this.#constructorName; } - - /** @member {boolean} */ - get isPrimitive(){ return this.#isPrimitive; } - - /** @member {boolean} */ - get isObject(){ return !this.#isPrimitive; } -} - - - -/** - * Determine the type of a value. - * @param {*} value - * @returns {Type} - */ -function is(value){ - - let typeName, - typeofType = typeof value, - toStringTag, - constructorName; - - if(value instanceof Object){ - toStringTag = Object.prototype.toString.call(value).slice(8, -1); - constructorName = value.constructor.name || ""; - } - - if(value === null) - typeName = "null"; - else if(Number.isNaN(value) || (value instanceof Number && Number.isNaN(value.valueOf()))) - typeName = "nan"; - else if(!["object", "function"].includes(typeofType)) - typeName = typeofType; - else{ - typeName = "object"; - for(const name in OBJECT_TYPES){ - if(value instanceof OBJECT_TYPES[name]){ - typeName = name; - break; + if(typeName === "numberish"){ + if(Number.isNaN(value) || (value instanceof Number && Number.isNaN(value.valueOf()))){ + typeName = "nan"; + } + else{ + typeName = "number"; } } + + super(typeName); + + for(const name in TYPES){ + this[name] = typeName === name; + } + + this.numberish = this.number || this.nan; + if(this.number){ + this.real = Number.isFinite(this.object ? value.valueOf() : value); + this.infinite = !this.real; + } + + this.primitive = !this.object; + this.objectish = this.object || this.null; + + this.defined = !this.undefined; + this.nullish = this.undefined || this.null; + + this.falsy = !value; + this.truthy = !this.falsy; + + if(this.string || this.array){ + this.empty = value.length === 0; + } + else if(this.map || this.set || this.weakmap || this.weakset){ + this.empty = value.size === 0; + } } - - return new Type(typeName, typeofType, toStringTag, constructorName); } - - -// Create type testers. - -for(const name of TYPE_NAMES){ - is[name] = (v)=> is(v).type === name; +function is(value){ + return new TypeTest(value); } -is.object = (v)=> is(v).isObject; -is.primitive = (v)=> is(v).isPrimitive; - -is.defined = (v)=> !is.undefined(v); -is.nullish = (v)=> (is.undefined(v) || is.null(v)); - -is.falsy = (v)=> !v; -is.truthy = (v)=> !!v; - -is.numberish = (v)=> (is.number(v) || is.nan(v)); -is.real = (v)=> (is.number(v) && Number.isFinite(v instanceof Number ? v.valueOf() : v)); -is.infinite = (v)=> (is.number(v) && !Number.isFinite(v instanceof Number ? v.valueOf() : v)); - - - -// Create additional methods. - -is.empty = (v)=> (v?.length === 0 || v?.size === 0); - -is.of = (v, c)=> v instanceof c; - - - export { is as default }; From 5efe6ec5d16aa959f1913ac318515f6f2003d6bb Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Sun, 5 Jan 2025 11:24:32 -0500 Subject: [PATCH 03/14] WIP --- isType.mjs.js | 23 +++++++++--- test/test.htm | 14 +++---- test/test.js | 101 +++++++++++++++++++++++++++----------------------- 3 files changed, 78 insertions(+), 60 deletions(-) diff --git a/isType.mjs.js b/isType.mjs.js index d752d3c..0ca1512 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -3,7 +3,7 @@ const TYPES = { array: { class: Array }, bigint: { primitive: "bigint" }, - boolean: { primitive: "boolean", class: Boolean }, + boolean: { primitive: "boolean", }, date: { class: Date }, error: { class: Error }, function: { class: Function }, @@ -26,9 +26,10 @@ const TYPES = { /** * A collection of boolean properties indicating the type of the given value. * @class TypeTest - * @extends {String} */ -class TypeTest extends String { +class TypeTest { + + #typeName; /** * @constructor @@ -62,7 +63,7 @@ class TypeTest extends String { } } - super(typeName); + this.#typeName = typeName; for(const name in TYPES){ this[name] = typeName === name; @@ -74,6 +75,7 @@ class TypeTest extends String { this.infinite = !this.real; } + this.object = value instanceof Object; this.primitive = !this.object; this.objectish = this.object || this.null; @@ -85,13 +87,24 @@ class TypeTest extends String { if(this.string || this.array){ this.empty = value.length === 0; + this.nonempty = !this.empty; } - else if(this.map || this.set || this.weakmap || this.weakset){ + else if(this.map || this.set){ this.empty = value.size === 0; + this.nonempty = !this.empty; } } + + toString(){ + return this.#typeName; + } } +/** + * Determine the type of a value. The returned object includes boolean properties to quickly test against specific types or for specific states (e.g., 'empty'). + * @param {*} value - The value to be tested. + * @returns {TypeTest} + */ function is(value){ return new TypeTest(value); } diff --git a/test/test.htm b/test/test.htm index 3545384..e561d34 100644 --- a/test/test.htm +++ b/test/test.htm @@ -26,18 +26,14 @@ -
Types +
Types - - - - - - - - + + + + diff --git a/test/test.js b/test/test.js index ca25f64..cbf5a00 100644 --- a/test/test.js +++ b/test/test.js @@ -1,76 +1,84 @@ import is from "../isType.mjs.js"; -function _is(value, pseudocode, _type, _typeof, _toStringTag, _constructorName){ +function _is(value, pseudocodeHTML, expectedType, expectedIsObject){ - const type = is(value); + const actualType = is(value); + //console.log(actualType); const tbody = document.getElementById("type_results"); const tr = document.createElement("tr"); - tr.innerHTML = ``; - createCell(type.type, _type); - createCell(type.typeof, _typeof); - createCell(type.toStringTag, _toStringTag); - createCell(type.constructorName, _constructorName); + tr.innerHTML = ``; + createCell(actualType, expectedType); + tbody.appendChild(tr); + createCell(actualType.object, expectedIsObject); tbody.appendChild(tr); + let props = ""; + for(const name in actualType){ + if(actualType[name]) props += `, ${name}`; + } + tr.innerHTML += ``; + function createCell(actual, expected){ const td = document.createElement("td"); - td.dataset.expected = expected === void 0 ? "undefined" : `"${expected}"`; - td.innerHTML = actual === void 0 ? `undefined` : `"${actual}"`; - if(actual !== expected) td.classList.add("fail"); + td.dataset.expected = expected === void 0 ? "undefined" : expected; + td.innerHTML = actual === void 0 ? `undefined` : actual.toString() === "" ? `empty string` : actual; + if(actual != expected) td.classList.add("fail"); tr.appendChild(td); } } { - _is([], "[]", "array", "object", "Array", "Array"); - _is(new Array(), "new Array()", "array", "object", "Array", "Array"); - _is(5n, "5n", "bigint", "bigint"); - _is(true, "true", "boolean", "boolean"); - _is(false, "false", "boolean", "boolean"); - _is(new Boolean(), "new Boolean()", "boolean", "object", "Boolean", "Boolean"); - _is(new Date(), "new Date()", "date", "object", "Date", "Date"); - _is(new Error(), "new Error()", "error", "object", "Error", "Error"); - _is(new TypeError(), "new TypeError()", "error", "object", "Error", "TypeError"); - _is(()=>{}, "()=>{}", "function", "function", "Function", "Function"); - _is(Object, "Object", "function", "function", "Function", "Function"); - _is(new Map(), "new Map()", "map", "object", "Map", "Map"); - _is(NaN, "NaN", "nan", "number"); - _is(new Number(NaN), "new Number(NaN)", "nan", "object", "Number", "Number"); - _is(new Number('a'), "new Number('a')", "nan", "object", "Number", "Number"); - _is(null, "null", "null", "object"); - _is(5, "5", "number", "number"); - _is(Infinity, "Infinity", "number", "number"); - _is(new Number(), "new Number()", "number", "object", "Number", "Number"); - _is({}, "{}", "object", "object", "Object", "Object"); - _is(new Object(), "new Object()", "object", "object", "Object", "Object"); - _is(new Promise(()=>{}), "new Promise(()=>{})", "promise", "object", "Promise", "Promise"); - _is(/a/, "/a/", "regex", "object", "RegExp", "RegExp"); - _is(new RegExp(), "new RegExp()", "regex", "object", "RegExp", "RegExp"); - _is(new Set(), "new Set()", "set", "object", "Set", "Set"); - _is("", '""', "string", "string"); - _is("a", '"a"', "string", "string"); - _is(new String(), "new String()", "string", "object", "String", "String"); - _is(Symbol(), "Symbol()", "symbol", "symbol"); - _is(void 0, "void 0", "undefined", "undefined"); - _is(new WeakMap(), "new WeakMap()", "weakmap", "object", "WeakMap", "WeakMap"); - _is(new WeakSet(), "new WeakSet()", "weakset", "object", "WeakSet", "WeakSet"); + _is([], "[]", "array", true); + _is(new Array(), "new Array()", "array", true); + _is([1,2], "[1,2]", "array", true); + _is(new Array(1,2), "new Array(1,2)", "array", true); + _is(5n, "5n", "bigint", false); + _is(true, "true", "boolean", false); + _is(false, "false", "boolean", false); + _is(new Boolean(), "new Boolean()", "object", true); + _is(new Date(), "new Date()", "date", true); + _is(new Error(), "new Error()", "error", true); + _is(new TypeError(), "new TypeError()", "error", true); + _is(()=>{}, "()=>{}", "function", true); + _is(Object, "Object", "function", true); + _is(new Map(), "new Map()", "map", true); + _is(NaN, "NaN", "nan", false); + _is(new Number(NaN), "new Number(NaN)", "nan", true); + _is(new Number('a'), "new Number('a')", "nan", true); + _is(null, "null", "null", false); + _is(5, "5", "number", false); + _is(Infinity, "Infinity", "number", false); + _is(new Number(), "new Number()", "number", true); + _is({}, "{}", "object", true); + _is(new Object(), "new Object()", "object", true); + _is(new Promise(()=>{}), "new Promise(()=>{})", "promise", true); + _is(/a/, "/a/", "regex", true); + _is(new RegExp(), "new RegExp()", "regex", true); + _is(new Set(), "new Set()", "set", true); + _is("", '""', "string", false); + _is("a", '"a"', "string", false); + _is(new String(), "new String()", "string", true); + _is(Symbol(), "Symbol()", "symbol", false); + _is(void 0, "void 0", "undefined", false); + _is(new WeakMap(), "new WeakMap()", "weakmap", true); + _is(new WeakSet(), "new WeakSet()", "weakset", true); { class Foo {} - _is(new Foo(), "class Foo {}
new Foo()", "object", "object", "Object", "Foo"); + _is(new Foo(), "class Foo {}
new Foo()", "object", true); } { class Foo extends String {} - _is(new Foo(), "class Foo extends String {}
new Foo()", "string", "object", "String", "Foo"); + _is(new Foo(), "class Foo extends String {}
new Foo()", "string", true); } { class Foo { get [Symbol.toStringTag](){ return "Bar"; } } - _is(new Foo(), "class Foo { get [Symbol.toStringTag](){ return \"Bar\"; } }
new Foo()", "object", "object", "Bar", "Foo"); + _is(new Foo(), "class Foo { get [Symbol.toStringTag](){ return \"Bar\"; } }
new Foo()", "object", true); } } - +/* function _tester(value, pseudocode, _testerNames){ const testers = [ @@ -191,3 +199,4 @@ function _more(test, pseudocode, _result){ _more(is.of(5, Number), "is.of(5, Number)", false); _more(is.of(new Number(5), Number), "is.of(new Number(5), Number)", true); } +*/ \ No newline at end of file From 0e342bbaee1cd2397d5ac92daa8f3069dd5d8b8b Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Sun, 5 Jan 2025 16:25:55 -0500 Subject: [PATCH 04/14] add `all` and `any` methods --- isType.mjs.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/isType.mjs.js b/isType.mjs.js index 0ca1512..b2cc521 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -23,6 +23,39 @@ const TYPES = { weakset: { class: WeakSet }, }; +const TYPE_TEST_PROPERTY_NAMES = [ + "array", + "bigint", + "boolean", + "date", + "defined", + "empty", + "error", + "falsy", + "function", + "infinite", + "map", + "nan", + "nonempty", + "null", + "nullish", + "number", + "numberish", + "object", + "objectish", + "primitive", + "promise", + "real", + "regex", + "set", + "string", + "symbol", + "truthy", + "undefined", + "weakmap", + "weakset" +]; + /** * A collection of boolean properties indicating the type of the given value. * @class TypeTest @@ -103,10 +136,21 @@ class TypeTest { /** * Determine the type of a value. The returned object includes boolean properties to quickly test against specific types or for specific states (e.g., 'empty'). * @param {*} value - The value to be tested. - * @returns {TypeTest} + * @returns {TypeTest} - Plus methods `all` and `any`. */ function is(value){ - return new TypeTest(value); + + const typeTest = new TypeTest(value); + + const tester = (propName)=>typeTest[propName]; + typeTest.all = (...propNames)=>propNames.every(tester); + typeTest.any = (...propNames)=>propNames.some(tester); + + return typeTest; } +TYPE_TEST_PROPERTY_NAMES.forEach((propName)=>{ + is[propName] = propName; +}); + export { is as default }; From 4eb87da94259e80bbd1f8247dd01432cb2f0d783 Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Sun, 12 Jan 2025 19:14:37 -0500 Subject: [PATCH 05/14] WIP on fewer-keystrokes --- isType.mjs.js | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/isType.mjs.js b/isType.mjs.js index b2cc521..4370d24 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -23,7 +23,7 @@ const TYPES = { weakset: { class: WeakSet }, }; -const TYPE_TEST_PROPERTY_NAMES = [ +const PROPERTY_NAMES = [ "array", "bigint", "boolean", @@ -57,7 +57,7 @@ const TYPE_TEST_PROPERTY_NAMES = [ ]; /** - * A collection of boolean properties indicating the type of the given value. + * A collection of boolean properties set according to the type, and sometimes value, of the argument. * @class TypeTest */ class TypeTest { @@ -115,6 +115,8 @@ class TypeTest { this.defined = !this.undefined; this.nullish = this.undefined || this.null; + this.false = value === false; + this.true = value === true; this.falsy = !value; this.truthy = !this.falsy; @@ -133,23 +135,35 @@ class TypeTest { } } +class Is extends TypeTest { + + constructor(value){ + + super(value); + + this.type = this.toString(); + } + + all(...propNames){ + return propNames.every(propName=>this[propName]); + } + + any(...propNames){ + return propNames.some(propName=>this[propName]); + } +} + /** * Determine the type of a value. The returned object includes boolean properties to quickly test against specific types or for specific states (e.g., 'empty'). * @param {*} value - The value to be tested. - * @returns {TypeTest} - Plus methods `all` and `any`. + * @returns {Is} */ function is(value){ - const typeTest = new TypeTest(value); - - const tester = (propName)=>typeTest[propName]; - typeTest.all = (...propNames)=>propNames.every(tester); - typeTest.any = (...propNames)=>propNames.some(tester); - - return typeTest; + return new Is(value); } -TYPE_TEST_PROPERTY_NAMES.forEach((propName)=>{ +PROPERTY_NAMES.forEach((propName)=>{ is[propName] = propName; }); From 7aef4031920bdacc4225c2f647fe00767e43ccb6 Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Sun, 12 Jan 2025 23:03:52 -0500 Subject: [PATCH 06/14] test generates HTML table --- isType.mjs.js | 47 ++-------- test/test.htm | 14 +++ test/test.js | 236 +++++++++++++++++++------------------------------- 3 files changed, 114 insertions(+), 183 deletions(-) diff --git a/isType.mjs.js b/isType.mjs.js index 4370d24..0cac393 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -23,39 +23,6 @@ const TYPES = { weakset: { class: WeakSet }, }; -const PROPERTY_NAMES = [ - "array", - "bigint", - "boolean", - "date", - "defined", - "empty", - "error", - "falsy", - "function", - "infinite", - "map", - "nan", - "nonempty", - "null", - "nullish", - "number", - "numberish", - "object", - "objectish", - "primitive", - "promise", - "real", - "regex", - "set", - "string", - "symbol", - "truthy", - "undefined", - "weakmap", - "weakset" -]; - /** * A collection of boolean properties set according to the type, and sometimes value, of the argument. * @class TypeTest @@ -128,6 +95,9 @@ class TypeTest { this.empty = value.size === 0; this.nonempty = !this.empty; } + else{ + this.empty = this.nonempty = void 0; + } } toString(){ @@ -138,10 +108,11 @@ class TypeTest { class Is extends TypeTest { constructor(value){ - super(value); - - this.type = this.toString(); + } + + get type(){ + return this.toString(); } all(...propNames){ @@ -163,8 +134,8 @@ function is(value){ return new Is(value); } -PROPERTY_NAMES.forEach((propName)=>{ +for(const propName in new TypeTest()){ is[propName] = propName; -}); +} export { is as default }; diff --git a/test/test.htm b/test/test.htm index e561d34..5dcaf58 100644 --- a/test/test.htm +++ b/test/test.htm @@ -20,6 +20,20 @@ display: block; color: #888; } + + #types th { + position: relative; + padding: 0 1.25em; + border: none; + } + #types th div { + position: absolute; + transform-origin: top left; + transform: rotate(-60deg); + left: calc(50% - 1.25em* cos(30deg)); + line-height: 1.25; + top: calc(-1em* sin(30deg)); + } diff --git a/test/test.js b/test/test.js index cbf5a00..08cec14 100644 --- a/test/test.js +++ b/test/test.js @@ -1,35 +1,104 @@ import is from "../isType.mjs.js"; +let testResults = []; -function _is(value, pseudocodeHTML, expectedType, expectedIsObject){ +{ + _is(()=>(void 0), is.undefined, is.primitive, is.nullish, is.falsy); + _is(()=>(new Number(Infinity)), is.number, is.defined, is.object, is.objectish, is.truthy, is.infinite, is.numberish); +} + +document.addEventListener("DOMContentLoaded", ()=>{document.body.innerHTML += createTable()}); + +/** + * Perform a test and add the result details to the `testResults` array. + * + * @param {function} fn - An arrow function returning the value to be tested. The function body is saved as a string for later use. + * @param {string} type - The expected type of the test value. + * @param {...string} [trues] - Names of all additional properties of the test result that are expected to evaluate as `true`. + */ +function _is(fn, type, ...trues){ - const actualType = is(value); - //console.log(actualType); + trues.unshift(type); + const code = fn.toString().slice(5,-1); + const value = fn(); + const test = is(value); - const tbody = document.getElementById("type_results"); - const tr = document.createElement("tr"); - tr.innerHTML = ``; - createCell(actualType, expectedType); - tbody.appendChild(tr); - createCell(actualType.object, expectedIsObject); - tbody.appendChild(tr); + const testResult = { + code: code, + type: { + expected: type, + actual: test.type + }, + properties: {}, + }; - let props = ""; - for(const name in actualType){ - if(actualType[name]) props += `, ${name}`; + for(const propName in is){ + + const expected = trues.includes(propName); + const actual = test[propName]; + + testResult.properties[propName] = { + expected: expected, + actual: actual + }; } - tr.innerHTML += ``; - function createCell(actual, expected){ - const td = document.createElement("td"); - td.dataset.expected = expected === void 0 ? "undefined" : expected; - td.innerHTML = actual === void 0 ? `undefined` : actual.toString() === "" ? `empty string` : actual; - if(actual != expected) td.classList.add("fail"); - tr.appendChild(td); + testResults.push(testResult); +} + +/** + * Create a table to show the results. + */ +function createTable(){ + + let rows = ""; + for(const result of testResults){ + rows += createRow(result); + } + + let header = ""; + for(const propName in is){ + header += ``; } + + return `
ValueType
.type.typeof.toStringTag.constructorNameValueTypeObjectProperties
${pseudocode}${pseudocodeHTML}${props.slice(2)}${pseudocodeHTML}${props.slice(2)}
type
${propName}
${header}${rows}
`; } -{ +/** + * Generate a row for the results table. + * + * @param {Object} result - An item in the `testResults` array. + */ +function createRow(result){ + + let typeMatch = result.type.expected !== result.type.actual; + + let code = result.code.replace("&","&").replace("<","<").replace(">",">").replace("\"","""); + + let cells = `${code}${result.type.actual}`; + if(typeMatch) cells += ` (expected ${result.type.expected})`; + cells += ``; + + for(const prop in result.properties){ + cells += createCell(result.properties[prop].expected, result.properties[prop].actual); + } + + return `${cells}`; +} + +/** + * Generate a cell for the results table. + * + * @param {boolean} expected + * @param {boolean} actual + */ +function createCell(expected, actual){ + + if(!(expected || actual)) return ``; + return `${expected ? "✔️" : "✖️"}`; +} + +/*{ _is([], "[]", "array", true); _is(new Array(), "new Array()", "array", true); _is([1,2], "[1,2]", "array", true); @@ -76,127 +145,4 @@ function _is(value, pseudocodeHTML, expectedType, expectedIsObject){ class Foo { get [Symbol.toStringTag](){ return "Bar"; } } _is(new Foo(), "class Foo { get [Symbol.toStringTag](){ return \"Bar\"; } }
new Foo()", "object", true); } -} - -/* -function _tester(value, pseudocode, _testerNames){ - - const testers = [ - "array", - "bigint", - "boolean", - "date", - "defined", - "error", - "falsy", - "function", - "infinite", - "map", - "nan", - "null", - "nullish", - "number", - "numberish", - "object", - "primitive", - "promise", - "real", - "regex", - "set", - "string", - "symbol", - "truthy", - "undefined", - "weakmap", - "weakset", - ]; - - const tbody = document.getElementById("tester_results"); - const tr = document.createElement("tr"); - tr.innerHTML = `${pseudocode}`; - const matches = []; - for(const tester of testers){ - const match = is[tester](value); - if(match) matches.push(tester); - } - createCell(matches.join(", "), _testerNames.sort().join(", ")); - tbody.appendChild(tr); - - function createCell(actual, expected){ - const td = document.createElement("td"); - td.dataset.expected = expected; - td.innerHTML = actual; - if(actual !== expected) td.classList.add("fail"); - tr.appendChild(td); - } -} - -{ - _tester([], "[]", ["array", "object", "defined", "truthy"]); - _tester(new Array(), "new Array()", ["array", "object", "defined", "truthy"]); - _tester(5n, "5n", ["bigint", "primitive", "defined", "truthy"]); - _tester(0n, "0n", ["bigint", "primitive", "defined", "falsy"]); - _tester(true, "true", ["boolean", "primitive", "defined", "truthy"]); - _tester(false, "false", ["boolean", "primitive", "defined", "falsy"]); - _tester(new Boolean(), "new Boolean()", ["boolean", "object", "defined", "truthy"]); - _tester(new Date(), "new Date()", ["date", "object", "defined", "truthy"]); - _tester(new Error(), "new Error()", ["error", "object", "defined", "truthy"]); - _tester(new TypeError(), "new TypeError()", ["error", "object", "defined", "truthy"]); - _tester(()=>{}, "()=>{}", ["function", "object", "defined", "truthy"]); - _tester(Object, "Object", ["function", "object", "defined", "truthy"]); - _tester(new Map(), "new Map()", ["map", "object", "defined", "truthy"]); - _tester(NaN, "NaN", ["nan", "numberish", "primitive", "defined", "falsy"]); - _tester(new Number(NaN), "new Number(NaN)", ["nan", "numberish", "object", "defined", "truthy"]); - _tester(new Number('a'), "new Number('a')", ["nan", "numberish", "object", "defined", "truthy"]); - _tester(null, "null", ["null", "nullish", "primitive", "defined", "falsy"]); - _tester(5, "5", ["number", "numberish", "real", "primitive", "defined", "truthy"]); - _tester(0, "0", ["number", "numberish", "real", "primitive", "defined", "falsy"]); - _tester(-0, "-0", ["number", "numberish", "real", "primitive", "defined", "falsy"]); - _tester(Infinity, "Infinity", ["number", "infinite", "numberish", "primitive", "defined", "truthy"]); - _tester(new Number(5), "new Number(5)", ["number", "numberish", "real", "object", "defined", "truthy"]); - _tester(new Number(0), "new Number(0)", ["number", "numberish", "real", "object", "defined", "truthy"]); - _tester({}, "{}", ["object", "defined", "truthy"]); - _tester(new Object(), "new Object()", ["object", "defined", "truthy"]); - _tester(new Promise(()=>{}), "new Promise(()=>{})", ["promise", "object", "defined", "truthy"]); - _tester(/a/, "/a/", ["regex", "object", "defined", "truthy"]); - _tester(new RegExp(), "new RegExp()", ["regex", "object", "defined", "truthy"]); - _tester(new Set(), "new Set()", ["set", "object", "defined", "truthy"]); - _tester("", '""', ["string", "primitive", "defined", "falsy"]); - _tester("a", '"a"', ["string", "primitive", "defined", "truthy"]); - _tester(new String(), "new String()", ["string", "object", "defined", "truthy"]); - _tester(Symbol(), "Symbol()", ["symbol", "primitive", "defined", "truthy"]); - _tester(void 0, "void 0", ["undefined", "nullish", "primitive", "falsy"]); - _tester(new WeakMap(), "new WeakMap()", ["weakmap", "object", "defined", "truthy"]); - _tester(new WeakSet(), "new WeakSet()", ["weakset", "object", "defined", "truthy"]); - _tester(document.all, "document.all", ["object", "nullish", "undefined", "falsy"]); -} - - -function _more(test, pseudocode, _result){ - - const tbody = document.getElementById("additional_results"); - const tr = document.createElement("tr"); - tr.innerHTML = `${pseudocode}`; - createCell(test, _result); - tbody.appendChild(tr); - - function createCell(actual, expected){ - const td = document.createElement("td"); - td.dataset.expected = expected; - td.innerHTML = actual; - if(actual !== expected) td.classList.add("fail"); - tr.appendChild(td); - } -} - -{ - _more(is.empty(""), 'is.empty("")', true); - _more(is.empty("a"), 'is.empty("a")', false); - _more(is.empty([]), "is.empty([])", true); - _more(is.empty([5]), "is.empty([5])", false); - _more(is.empty(new Map()), "is.empty(new Map())", true); - _more(is.empty(new Map([[1, "one"]])), 'is.empty(new Map([[1, "one"]]))', false); - _more(is.of(5, Number), "is.of(5, Number)", false); - _more(is.of(new Number(5), Number), "is.of(new Number(5), Number)", true); -} -*/ \ No newline at end of file +}*/ From 03aa0fdf5f9f24ea63960f09352bb162db3f65ed Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Mon, 13 Jan 2025 04:40:37 -0500 Subject: [PATCH 07/14] WIP on fewer-keystrokes --- isType.mjs.js | 2 +- test/test.htm | 30 +++++++++++++++++++++++++++--- test/test.js | 12 +++++++----- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/isType.mjs.js b/isType.mjs.js index 0cac393..7598dae 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -96,7 +96,7 @@ class TypeTest { this.nonempty = !this.empty; } else{ - this.empty = this.nonempty = void 0; + this.empty = this.nonempty = false; } } diff --git a/test/test.htm b/test/test.htm index 5dcaf58..520f403 100644 --- a/test/test.htm +++ b/test/test.htm @@ -21,18 +21,42 @@ color: #888; } + #types { + font-family: sans-serif; + margin-top: 5em; + } #types th { position: relative; - padding: 0 1.25em; + padding: 0 0.625em; border: none; } #types th div { position: absolute; transform-origin: top left; transform: rotate(-60deg); - left: calc(50% - 1.25em* cos(30deg)); + left: calc(50% - 1em * cos(30deg)); line-height: 1.25; - top: calc(-1em* sin(30deg)); + top: calc(-1em * sin(30deg) - 0.25em); + } + #types td { + padding: 0 0.25em 0.25em; + min-width: 1.375em; + vertical-align: middle; + white-space: nowrap; + text-align: left; + background: #90ee9022; + } + #types td:nth-child(n+3) { + text-align: center; + } + #types td:first-child { + background: none; + } + #types td.match { + background: lightgreen; + } + #types td.error { + background: lightcoral; } diff --git a/test/test.js b/test/test.js index 08cec14..d55fb09 100644 --- a/test/test.js +++ b/test/test.js @@ -5,6 +5,7 @@ let testResults = []; { _is(()=>(void 0), is.undefined, is.primitive, is.nullish, is.falsy); _is(()=>(new Number(Infinity)), is.number, is.defined, is.object, is.objectish, is.truthy, is.infinite, is.numberish); + _is(()=>([]), is.array, is.defined, is.object, is.objectish, is.truthy, is.empty); } document.addEventListener("DOMContentLoaded", ()=>{document.body.innerHTML += createTable()}); @@ -12,7 +13,7 @@ document.addEventListener("DOMContentLoaded", ()=>{document.body.innerHTML += cr /** * Perform a test and add the result details to the `testResults` array. * - * @param {function} fn - An arrow function returning the value to be tested. The function body is saved as a string for later use. + * @param {function} fn - An arrow function, in the form of `()=>(...)`, returning the value to be tested. The function body is saved as a string for later use. * @param {string} type - The expected type of the test value. * @param {...string} [trues] - Names of all additional properties of the test result that are expected to evaluate as `true`. */ @@ -71,12 +72,12 @@ function createTable(){ */ function createRow(result){ - let typeMatch = result.type.expected !== result.type.actual; + let typeMatch = result.type.expected === result.type.actual; let code = result.code.replace("&","&").replace("<","<").replace(">",">").replace("\"","""); - let cells = `${code}${result.type.actual}`; - if(typeMatch) cells += ` (expected ${result.type.expected})`; + let cells = `${code}${result.type.actual}`; + if(!typeMatch) cells += `
(expected ${result.type.expected})`; cells += ``; for(const prop in result.properties){ @@ -95,7 +96,8 @@ function createRow(result){ function createCell(expected, actual){ if(!(expected || actual)) return ``; - return `${expected ? "✔️" : "✖️"}`; + if(expected === actual) return `✔️`; + return `${actual ? "✔️" : ""}`; } /*{ From 3e6fa0ec7fe31930416c8d673e8f809df642e776 Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Mon, 13 Jan 2025 22:16:48 -0500 Subject: [PATCH 08/14] test page done --- test/test.htm | 90 ++++++++++++++----------------------------- test/test.js | 103 +++++++++++++++++++++++++------------------------- 2 files changed, 79 insertions(+), 114 deletions(-) diff --git a/test/test.htm b/test/test.htm index 520f403..5b29aa2 100644 --- a/test/test.htm +++ b/test/test.htm @@ -3,104 +3,70 @@ isType tests -
Types - - - - - - - - - - -
ValueTypeObjectProperties
-
- -
Type Tester Methods - - - - - - - - -
ValueSuccessful Type Tester Methods
-
- -
Additional Methods - - - - - - - - -
TestResult
-
+
diff --git a/test/test.js b/test/test.js index d55fb09..703fcd8 100644 --- a/test/test.js +++ b/test/test.js @@ -4,11 +4,59 @@ let testResults = []; { _is(()=>(void 0), is.undefined, is.primitive, is.nullish, is.falsy); + _is(()=>(null), is.null, is.defined, is.primitive, is.nullish, is.objectish, is.falsy); + _is(()=>(false), is.boolean, is.defined, is.primitive, is.false, is.falsy); + _is(()=>(true), is.boolean, is.defined, is.primitive, is.true, is.truthy); + _is(()=>(new Boolean(false)), is.object, is.defined, is.objectish, is.truthy); + _is(()=>(0n), is.bigint, is.defined, is.primitive, is.falsy); + _is(()=>(5n), is.bigint, is.defined, is.primitive, is.truthy); + _is(()=>(0), is.number, is.defined, is.primitive, is.falsy, is.real, is.numberish); + _is(()=>(5), is.number, is.defined, is.primitive, is.truthy, is.real, is.numberish); + _is(()=>(new Number(0)), is.number, is.defined, is.object, is.objectish, is.truthy, is.real, is.numberish); + _is(()=>(new Number(5)), is.number, is.defined, is.object, is.objectish, is.truthy, is.real, is.numberish); + _is(()=>(Infinity), is.number, is.defined, is.primitive, is.truthy, is.infinite, is.numberish); + _is(()=>(-1/0), is.number, is.defined, is.primitive, is.truthy, is.infinite, is.numberish); _is(()=>(new Number(Infinity)), is.number, is.defined, is.object, is.objectish, is.truthy, is.infinite, is.numberish); + _is(()=>(NaN), is.nan, is.defined, is.primitive, is.falsy, is.numberish); + _is(()=>(+"a"), is.nan, is.defined, is.primitive, is.falsy, is.numberish); + _is(()=>(new Number(NaN)), is.nan, is.defined, is.object, is.objectish, is.truthy, is.numberish); + _is(()=>(new Number("a")), is.nan, is.defined, is.object, is.objectish, is.truthy, is.numberish); + _is(()=>(""), is.string, is.defined, is.primitive, is.falsy, is.empty); + _is(()=>("a"), is.string, is.defined, is.primitive, is.truthy, is.nonempty); + _is(()=>(new String("")), is.string, is.defined, is.object, is.objectish, is.truthy, is.empty); + _is(()=>(new String("a")), is.string, is.defined, is.object, is.objectish, is.truthy, is.nonempty); + _is(()=>(Symbol()), is.symbol, is.defined, is.primitive, is.truthy); _is(()=>([]), is.array, is.defined, is.object, is.objectish, is.truthy, is.empty); + _is(()=>([1,2]), is.array, is.defined, is.object, is.objectish, is.truthy, is.nonempty); + _is(()=>(new Array()), is.array, is.defined, is.object, is.objectish, is.truthy, is.empty); + _is(()=>(new Array(1,2)), is.array, is.defined, is.object, is.objectish, is.truthy, is.nonempty); + _is(()=>(new Map()), is.map, is.defined, is.object, is.objectish, is.truthy, is.empty); + _is(()=>((new Map()).set("a",1)), is.map, is.defined, is.object, is.objectish, is.truthy, is.nonempty); + _is(()=>(new Set()), is.set, is.defined, is.object, is.objectish, is.truthy, is.empty); + _is(()=>(new Set([1,2])), is.set, is.defined, is.object, is.objectish, is.truthy, is.nonempty); + _is(()=>(new WeakMap()), is.weakmap, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(new WeakSet()), is.weakset, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(new Date()), is.date, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(new Error()), is.error, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(new TypeError()), is.error, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(()=>{}), is.function, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(new Function("")), is.function, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(Object), is.function, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(new Promise(()=>{})), is.promise, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(/a/), is.regex, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(new RegExp("a")), is.regex, is.defined, is.object, is.objectish, is.truthy); + _is(()=>({}), is.object, is.defined, is.object, is.objectish, is.truthy); + _is(()=>(new Object()), is.object, is.defined, is.objectish, is.truthy); + class A{} + _is(()=>(/*class A{}*/ new A()), is.object, is.defined, is.objectish, is.truthy); + class B extends String{} + _is(()=>(/*class B extends String{}*/ new B()), is.string, is.defined, is.object, is.objectish, is.truthy, is.empty); + class C { get [Symbol.toStringTag](){ return "D"; } } + _is(()=>(/*class C {get [Symbol.toStringTag](){return "D"}}*/ new C()), is.object, is.defined, is.objectish, is.truthy); + } -document.addEventListener("DOMContentLoaded", ()=>{document.body.innerHTML += createTable()}); +document.addEventListener("DOMContentLoaded", ()=>{document.querySelector("#tests").innerHTML = createTableContent()}); /** * Perform a test and add the result details to the `testResults` array. @@ -50,7 +98,7 @@ function _is(fn, type, ...trues){ /** * Create a table to show the results. */ -function createTable(){ +function createTableContent(){ let rows = ""; for(const result of testResults){ @@ -62,7 +110,7 @@ function createTable(){ header += `
${propName}
`; } - return `${header}${rows}
`; + return `${header}${rows}`; } /** @@ -99,52 +147,3 @@ function createCell(expected, actual){ if(expected === actual) return `✔️`; return `${actual ? "✔️" : ""}`; } - -/*{ - _is([], "[]", "array", true); - _is(new Array(), "new Array()", "array", true); - _is([1,2], "[1,2]", "array", true); - _is(new Array(1,2), "new Array(1,2)", "array", true); - _is(5n, "5n", "bigint", false); - _is(true, "true", "boolean", false); - _is(false, "false", "boolean", false); - _is(new Boolean(), "new Boolean()", "object", true); - _is(new Date(), "new Date()", "date", true); - _is(new Error(), "new Error()", "error", true); - _is(new TypeError(), "new TypeError()", "error", true); - _is(()=>{}, "()=>{}", "function", true); - _is(Object, "Object", "function", true); - _is(new Map(), "new Map()", "map", true); - _is(NaN, "NaN", "nan", false); - _is(new Number(NaN), "new Number(NaN)", "nan", true); - _is(new Number('a'), "new Number('a')", "nan", true); - _is(null, "null", "null", false); - _is(5, "5", "number", false); - _is(Infinity, "Infinity", "number", false); - _is(new Number(), "new Number()", "number", true); - _is({}, "{}", "object", true); - _is(new Object(), "new Object()", "object", true); - _is(new Promise(()=>{}), "new Promise(()=>{})", "promise", true); - _is(/a/, "/a/", "regex", true); - _is(new RegExp(), "new RegExp()", "regex", true); - _is(new Set(), "new Set()", "set", true); - _is("", '""', "string", false); - _is("a", '"a"', "string", false); - _is(new String(), "new String()", "string", true); - _is(Symbol(), "Symbol()", "symbol", false); - _is(void 0, "void 0", "undefined", false); - _is(new WeakMap(), "new WeakMap()", "weakmap", true); - _is(new WeakSet(), "new WeakSet()", "weakset", true); - { - class Foo {} - _is(new Foo(), "class Foo {}
new Foo()", "object", true); - } - { - class Foo extends String {} - _is(new Foo(), "class Foo extends String {}
new Foo()", "string", true); - } - { - class Foo { get [Symbol.toStringTag](){ return "Bar"; } } - _is(new Foo(), "class Foo { get [Symbol.toStringTag](){ return \"Bar\"; } }
new Foo()", "object", true); - } -}*/ From 6f987e4352d4de847a81b4dc27e67384bf31f777 Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Thu, 23 Jan 2025 19:18:23 -0500 Subject: [PATCH 09/14] test page column order, fix number types --- isType.mjs.js | 11 +++++---- test/test.js | 64 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/isType.mjs.js b/isType.mjs.js index 7598dae..9ba61a6 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -69,15 +69,18 @@ class TypeTest { this[name] = typeName === name; } + this.object = value instanceof Object; + this.primitive = !this.object; + this.objectish = this.object || this.null; + this.numberish = this.number || this.nan; if(this.number){ this.real = Number.isFinite(this.object ? value.valueOf() : value); this.infinite = !this.real; } - - this.object = value instanceof Object; - this.primitive = !this.object; - this.objectish = this.object || this.null; + else{ + this.real = this.infinite = false; + } this.defined = !this.undefined; this.nullish = this.undefined || this.null; diff --git a/test/test.js b/test/test.js index 703fcd8..aa64f74 100644 --- a/test/test.js +++ b/test/test.js @@ -1,5 +1,42 @@ import is from "../isType.mjs.js"; +window.is = is; + +const COLUMNS = [ + "defined", + "undefined", + "primitive", + "object", + "objectish", + "null", + "nullish", + "boolean", + "false", + "true", + "falsy", + "truthy", + "symbol", + "bigint", + "numberish", + "nan", + "number", + "real", + "infinite", + "string", + "array", + "map", + "set", + "weakmap", + "weakset", + "empty", + "nonempty", + "date", + "error", + "function", + "promise", + "regex", +]; + let testResults = []; { @@ -8,8 +45,14 @@ let testResults = []; _is(()=>(false), is.boolean, is.defined, is.primitive, is.false, is.falsy); _is(()=>(true), is.boolean, is.defined, is.primitive, is.true, is.truthy); _is(()=>(new Boolean(false)), is.object, is.defined, is.objectish, is.truthy); + _is(()=>(Symbol()), is.symbol, is.defined, is.primitive, is.truthy); + _is(()=>(Symbol.toStringTag), is.symbol, is.defined, is.primitive, is.truthy); _is(()=>(0n), is.bigint, is.defined, is.primitive, is.falsy); _is(()=>(5n), is.bigint, is.defined, is.primitive, is.truthy); + _is(()=>(NaN), is.nan, is.defined, is.primitive, is.falsy, is.numberish); + _is(()=>(+"a"), is.nan, is.defined, is.primitive, is.falsy, is.numberish); + _is(()=>(new Number(NaN)), is.nan, is.defined, is.object, is.objectish, is.truthy, is.numberish); + _is(()=>(new Number("a")), is.nan, is.defined, is.object, is.objectish, is.truthy, is.numberish); _is(()=>(0), is.number, is.defined, is.primitive, is.falsy, is.real, is.numberish); _is(()=>(5), is.number, is.defined, is.primitive, is.truthy, is.real, is.numberish); _is(()=>(new Number(0)), is.number, is.defined, is.object, is.objectish, is.truthy, is.real, is.numberish); @@ -17,15 +60,10 @@ let testResults = []; _is(()=>(Infinity), is.number, is.defined, is.primitive, is.truthy, is.infinite, is.numberish); _is(()=>(-1/0), is.number, is.defined, is.primitive, is.truthy, is.infinite, is.numberish); _is(()=>(new Number(Infinity)), is.number, is.defined, is.object, is.objectish, is.truthy, is.infinite, is.numberish); - _is(()=>(NaN), is.nan, is.defined, is.primitive, is.falsy, is.numberish); - _is(()=>(+"a"), is.nan, is.defined, is.primitive, is.falsy, is.numberish); - _is(()=>(new Number(NaN)), is.nan, is.defined, is.object, is.objectish, is.truthy, is.numberish); - _is(()=>(new Number("a")), is.nan, is.defined, is.object, is.objectish, is.truthy, is.numberish); _is(()=>(""), is.string, is.defined, is.primitive, is.falsy, is.empty); _is(()=>("a"), is.string, is.defined, is.primitive, is.truthy, is.nonempty); _is(()=>(new String("")), is.string, is.defined, is.object, is.objectish, is.truthy, is.empty); _is(()=>(new String("a")), is.string, is.defined, is.object, is.objectish, is.truthy, is.nonempty); - _is(()=>(Symbol()), is.symbol, is.defined, is.primitive, is.truthy); _is(()=>([]), is.array, is.defined, is.object, is.objectish, is.truthy, is.empty); _is(()=>([1,2]), is.array, is.defined, is.object, is.objectish, is.truthy, is.nonempty); _is(()=>(new Array()), is.array, is.defined, is.object, is.objectish, is.truthy, is.empty); @@ -51,8 +89,6 @@ let testResults = []; _is(()=>(/*class A{}*/ new A()), is.object, is.defined, is.objectish, is.truthy); class B extends String{} _is(()=>(/*class B extends String{}*/ new B()), is.string, is.defined, is.object, is.objectish, is.truthy, is.empty); - class C { get [Symbol.toStringTag](){ return "D"; } } - _is(()=>(/*class C {get [Symbol.toStringTag](){return "D"}}*/ new C()), is.object, is.defined, is.objectish, is.truthy); } @@ -100,17 +136,17 @@ function _is(fn, type, ...trues){ */ function createTableContent(){ + let header = "
type
"; + for(const propName of COLUMNS){ + header += `
${propName}
`; + } + let rows = ""; for(const result of testResults){ rows += createRow(result); } - let header = "
type
"; - for(const propName in is){ - header += `
${propName}
`; - } - - return `${header}${rows}`; + return `${header}${rows}`; } /** @@ -128,7 +164,7 @@ function createRow(result){ if(!typeMatch) cells += `
(expected ${result.type.expected})`; cells += ``; - for(const prop in result.properties){ + for(const prop of COLUMNS){ cells += createCell(result.properties[prop].expected, result.properties[prop].actual); } From 7e6f49b79c08843832002acf95673eee20c7599a Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Thu, 23 Jan 2025 20:18:29 -0500 Subject: [PATCH 10/14] WIP readme --- README.md | 55 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 1011259..7df603c 100644 --- a/README.md +++ b/README.md @@ -13,27 +13,40 @@ See the [test page](https://wizard04wsu.github.io/javascript-type-testing/test/t This module uses an expanded set of type names that make no distinction between primitive values and objects. For example, `5` and `new Number(5)` are both of type "number". -| Type Name | Values -| - | - -| array | `Array` objects -| bigint | _bigint_ primitives -| boolean | `false`, `true`, `Boolean` objects -| date | `Date` objects -| error | `Error` objects -| function | `Function` objects -| map | `Map` objects -| nan | `NaN`, `Number` objects with value `NaN` -| null | `null` -| number | _number_ primitives, `Number` objects; excludes `NaN` values -| object | instances of `Object` that don't match another type in this list -| promise | `Promise` objects -| regex | `RegExp` objects -| set | `Set` objects -| string | _string_ primitives, `String` objects -| symbol | _symbol_ primitives -| undefined | `undefined` -| weakmap | `WeakMap` objects -| weakset | `WeakSet` objects +| Type Name | Primitive Values | Instances Of Classes +| - | - | - +| defined | any value (not undefined) | `Object` +| undefined | undefined | +| primitive | not an instance of `Object` | +| object | | `Object` +| objectish | `null` | `Object` +| null | `null` | +| nullish | undefined, `null` | +| boolean | `false`, `true` | +| false | `false` | +| true | `true` | +| falsy | undefined, `null`, `false`, `0n`, `NaN`, `0`, `""` | +| truthy | values that are not _falsy_ | `Object` +| symbol | a `Symbol` | +| bigint | `0n`, `5n` | +| numberish | `0`, `5`, `Infinity`, `NaN` | `Number` +| nan | `NaN` | `Number` instances with value `NaN` +| number | `0`, `5`, `Infinity` | `Number` excluding instances with value `NaN` +| real | `0`, `5` | `Number` instances for real numbers +| infinite | `Infinity` | `Number` instances for infinite numbers +| string | `""`, `"foo"` | `String` +| array | `[]`, `[1,2]` | `Array` +| map | | `Map` +| set | | `Set` +| weakmap | | `WeakMap` +| weakset | | `WeakSet` +| empty | `[]` | `String` or `Array` of length == 0, `Map` or `Set` of size == 0 +| nonempty | values that are not _empty_ | instances that are not _empty_ +| date | | `Date` +| error | | `Error` +| function | | `Function`, `function(){}`, `()=>{}` +| promise | | `Promise` +| regex | | `Regex`, `/foo/` ## Determine a Type From d25f91188350fd5bbc0c1c1e5d5f534b7c3fa1e6 Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Thu, 23 Jan 2025 20:26:35 -0500 Subject: [PATCH 11/14] update readme --- README.md | 66 +++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 7df603c..b07337e 100644 --- a/README.md +++ b/README.md @@ -13,40 +13,40 @@ See the [test page](https://wizard04wsu.github.io/javascript-type-testing/test/t This module uses an expanded set of type names that make no distinction between primitive values and objects. For example, `5` and `new Number(5)` are both of type "number". -| Type Name | Primitive Values | Instances Of Classes +| Type Name | Primitive Values | Instances Of Classes | - | - | - -| defined | any value (not undefined) | `Object` -| undefined | undefined | -| primitive | not an instance of `Object` | -| object | | `Object` -| objectish | `null` | `Object` -| null | `null` | -| nullish | undefined, `null` | -| boolean | `false`, `true` | -| false | `false` | -| true | `true` | -| falsy | undefined, `null`, `false`, `0n`, `NaN`, `0`, `""` | -| truthy | values that are not _falsy_ | `Object` -| symbol | a `Symbol` | -| bigint | `0n`, `5n` | -| numberish | `0`, `5`, `Infinity`, `NaN` | `Number` -| nan | `NaN` | `Number` instances with value `NaN` -| number | `0`, `5`, `Infinity` | `Number` excluding instances with value `NaN` -| real | `0`, `5` | `Number` instances for real numbers -| infinite | `Infinity` | `Number` instances for infinite numbers -| string | `""`, `"foo"` | `String` -| array | `[]`, `[1,2]` | `Array` -| map | | `Map` -| set | | `Set` -| weakmap | | `WeakMap` -| weakset | | `WeakSet` -| empty | `[]` | `String` or `Array` of length == 0, `Map` or `Set` of size == 0 -| nonempty | values that are not _empty_ | instances that are not _empty_ -| date | | `Date` -| error | | `Error` -| function | | `Function`, `function(){}`, `()=>{}` -| promise | | `Promise` -| regex | | `Regex`, `/foo/` +| defined | any value (not undefined) | `Object` +| **undefined** | undefined | +| primitive | not an instance of `Object` | +| **object** | | `Object` +| objectish | `null` | `Object` +| **null** | `null` | +| nullish | undefined, `null` | +| **boolean** | `false`, `true` | +| false | `false` | +| true | `true` | +| falsy | undefined, `null`, `false`, `0n`, `NaN`, `0`, `""` | +| truthy | values that are not _falsy_ | `Object` +| **symbol** | a `Symbol` | +| **bigint** | `0n`, `5n` | +| numberish | `0`, `5`, `Infinity`, `NaN` | `Number` +| **nan** | `NaN` | `Number` instances with value `NaN` +| **number** | `0`, `5`, `Infinity` | `Number` excluding instances with value `NaN` +| real | `0`, `5` | `Number` instances for real numbers +| infinite | `Infinity` | `Number` instances for infinite numbers +| **string** | `""`, `"foo"` | `String` +| **array** | `[]`, `[1,2]` | `Array` +| **map** | | `Map` +| **set** | | `Set` +| **weakmap** | | `WeakMap` +| **weakset** | | `WeakSet` +| empty | `[]` | `String` or `Array` of length == 0, `Map` or `Set` of size == 0 +| nonempty | values that are not _empty_ | instances that are not _empty_ +| **date** | | `Date` +| **error** | | `Error` +| **function** | | `Function`, `function(){}`, `()=>{}` +| **promise** | | `Promise` +| **regex** | | `Regex`, `/foo/` ## Determine a Type From 41f230d23b14aaaee6ca0890898bd49c71f0c9e4 Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Thu, 23 Jan 2025 23:03:40 -0500 Subject: [PATCH 12/14] update readme, add .of() (untested) --- README.md | 182 +++++++++++++++----------------------------------- isType.mjs.js | 18 +++-- 2 files changed, 69 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index b07337e..b89c90b 100644 --- a/README.md +++ b/README.md @@ -8,140 +8,68 @@ See the [test page](https://wizard04wsu.github.io/javascript-type-testing/test/t --- -## Type Names - -This module uses an expanded set of type names that make no distinction between primitive values and objects. -For example, `5` and `new Number(5)` are both of type "number". - -| Type Name | Primitive Values | Instances Of Classes -| - | - | - -| defined | any value (not undefined) | `Object` -| **undefined** | undefined | -| primitive | not an instance of `Object` | -| **object** | | `Object` -| objectish | `null` | `Object` -| **null** | `null` | -| nullish | undefined, `null` | -| **boolean** | `false`, `true` | -| false | `false` | -| true | `true` | -| falsy | undefined, `null`, `false`, `0n`, `NaN`, `0`, `""` | -| truthy | values that are not _falsy_ | `Object` -| **symbol** | a `Symbol` | -| **bigint** | `0n`, `5n` | -| numberish | `0`, `5`, `Infinity`, `NaN` | `Number` -| **nan** | `NaN` | `Number` instances with value `NaN` -| **number** | `0`, `5`, `Infinity` | `Number` excluding instances with value `NaN` -| real | `0`, `5` | `Number` instances for real numbers -| infinite | `Infinity` | `Number` instances for infinite numbers -| **string** | `""`, `"foo"` | `String` -| **array** | `[]`, `[1,2]` | `Array` -| **map** | | `Map` -| **set** | | `Set` -| **weakmap** | | `WeakMap` -| **weakset** | | `WeakSet` -| empty | `[]` | `String` or `Array` of length == 0, `Map` or `Set` of size == 0 -| nonempty | values that are not _empty_ | instances that are not _empty_ -| **date** | | `Date` -| **error** | | `Error` -| **function** | | `Function`, `function(){}`, `()=>{}` -| **promise** | | `Promise` -| **regex** | | `Regex`, `/foo/` - - -## Determine a Type - -The **is()** function returns an object describing the type of its argument. +This module uses an expanded set of type names and related descriptors to simplify common tests of values. +Basic types do not distinguish between primitives and objects, but descriptors _primitive_ or _object_ +can be used to determine that aspect. +For example, `5` and `new Number(5)` are both _number_, but `5` is _primitive_ and `new Number(5)` is _object_. -Syntax: -> **is**(_value_) - -Returned object: -| Property | Description -| - | - -| .**type** | The type name used by this module. -| .**typeof** | The value returned by the `typeof` operator. -| .**toStringTag** | The name used by `Object.prototype.toString()`. `undefined` for primitives. -| .**constructorName** | The name of the argument's constructor. `undefined` for primitives. -| .**isObject** | True if the value is an object. -| .**isPrimitive** | True if the value is a primitive. - - -## Type Testing - -Each of the type-testing methods return a boolean indicating if the argument is of that type. +The **is()** function returns an **IsType** object describing its argument. Syntax: -> is._typeTester_(_value_) - -### Basics - -| Method | Tests for -| - | - -| is.**function**() | instance of `Function` -| is.**object**() | instance of `Object` -| is.**primitive**() | primitives -| is.**null**() | `null` -| is.**nullish**() | `undefined`, `null` -| is.**undefined**() | `undefined` -| is.**defined**() | not `undefined` - -### Booleans +> **is**(_value_) -| Method | Tests for -| - | - -| is.**boolean**() | `false`, `true`, instance of `Boolean` -| is.**falsy**() | `false`, `undefined`, `null`, `NaN`, `0`, `-0`, `0n`, `""`, [`document.all`](https://developer.mozilla.org/en-US/docs/Web/API/Document/all#conversion_to_boolean) -| is.**truthy**() | not falsy -### Numbers +## IsType Object -| Method | Tests for +| Member | Description | - | - -| is.**bigint**() | _bigint_ primitive -| is.**date**() | instance of `Date` -| is.**numberish**() | _number_ primitive, instance of `Number` - -_Numberish_ values can be more explicitly tested using the following methods: +| .type | The type of _value_ (using this module's type names). +| .of(_class_) | Tests if _value_ was an instance of _class_. +| .all(_...descriptors)_ | Takes a list of descriptor names as arguments. Returns `true` if **all** of them applied to _value_. +| .any(_...descriptors_) | Takes a list of descriptor names as arguments. Returns `true` if **any** of them applied to _value_. +| \[[_descriptor_](#type-names-and-related-descriptors)\] | Descriptors are listed in the table below. Each descriptor property is a boolean that is `true` if it applied to _value_. + +Enumerable properties of the **is** function are string values of the name of each descriptor. These can be used +in the `.all()` and `.any()` methods instead of string literals. +For example, `is(_value_).all("number", "object")` is equivalent to `is(_value_).all(is.number, is.object)`. + + +## Type Names and Related Descriptors + +| Descriptor | Type Name | Primitive Values | Instances Of Classes +| - | - | - | - +| defined | | not undefined | `Object` +| **undefined** | yes | undefined | +| primitive | | not an instance of `Object` | +| **object** | yes | | `Object` +| objectish | | `null` | `Object` +| **null** | yes | `null` | +| nullish | | undefined, `null` | +| **boolean** | yes | `false`, `true` | +| false | | `false` | +| true | | `true` | +| falsy | | undefined, `null`, `false`, `0n`, `NaN`, `0`, `""` | +| truthy | | not _falsy_ | `Object` +| **symbol** | yes | a `Symbol` | +| **bigint** | yes | `0n`, `5n` | +| numberish | | `0`, `5`, `Infinity`, `NaN` | `Number` +| **nan** | yes | `NaN` | `Number` with value `NaN` +| **number** | yes | `0`, `5`, `Infinity` | `Number` excluding those with value `NaN` +| real | | `0`, `5` | `Number` with a real number value +| infinite | | `Infinity` | `Number` with an infinite value +| **string** | yes | `""`, `"foo"` | `String` +| **array** | yes | `[]`, `[1,2]` | `Array` +| **map** | yes | | `Map` +| **set** | yes | | `Set` +| **weakmap** | yes | | `WeakMap` +| **weakset** | yes | | `WeakSet` +| empty | | `""`, `[]` | `String` or `Array` of length == 0, `Map` or `Set` of size == 0 +| nonempty | | not _empty_ | `String`, `Array`, `Map`, or `Set` that is not _empty_ +| **date** | yes | | `Date` +| **error** | yes | | `Error` +| **function** | yes | | `Function`, `function(){}`, `()=>{}` +| **promise** | yes | | `Promise` +| **regex** | yes | | `Regex`, `/foo/` -| Method | Tests for -| - | - -| is.**real**() | real numbers -| is.**infinite**() | `Infinity`, `-Infinity` -| is.**number**() | real numbers, `Infinity`, `-Infinity` -| is.**nan**() | `NaN` Note that JavaScript doesn't always treat mathematical expressions of undefined or indeterminate form as you might expect. For example, `1/0` is an undefined form, but JavaScript evaluates it as `Infinity`. - -### Text - -| Method | Tests for -| - | - -| is.**regex**() | instance of `RegExp` -| is.**string**() | _string_ primitive, instance of `String` - -### Collections - -| Method | Tests for -| - | - -| is.**array**() | instance of `Array` -| is.**map**() | instance of `Map` -| is.**set**() | instance of `Set` -| is.**weakmap**() | instance of `WeakMap` -| is.**weakset**() | instance of `WeakSet` - -### Other Common Types - -| Method | Tests for -| - | - -| is.**error**() | instance of `Error` -| is.**promise**() | instance of `Promise` -| is.**symbol**() | _symbol_ primitive - - -## Additional Methods - -| Method | Description -| - | - -| is.**empty**(_value_) | Tests for an empty _string_ primitive or if an object's `.length` or `.size` property equals zero. -| is.**of**(_value_, _class_) | Tests if _value_ is an instance of _class_. (Same as using the `instanceof` operator.) diff --git a/isType.mjs.js b/isType.mjs.js index 9ba61a6..8f50db7 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -108,16 +108,23 @@ class TypeTest { } } -class Is extends TypeTest { +class IsType extends TypeTest { + + #valueConstructor; constructor(value){ super(value); + if(value instanceof Object) this.#valueConstructor = value.constructor; } get type(){ return this.toString(); } + of(constructor){ + return this.#valueConstructor && (this.#valueConstructor === constructor || this.#valueConstructor.prototype instanceof constructor); + } + all(...propNames){ return propNames.every(propName=>this[propName]); } @@ -130,15 +137,18 @@ class Is extends TypeTest { /** * Determine the type of a value. The returned object includes boolean properties to quickly test against specific types or for specific states (e.g., 'empty'). * @param {*} value - The value to be tested. - * @returns {Is} + * @returns {IsType} */ function is(value){ - return new Is(value); + return new IsType(value); } for(const propName in new TypeTest()){ - is[propName] = propName; + Object.defineProperty(is, propName, { + value: propName, + enumerable: true, + }); } export { is as default }; From 000cfc3dc1527070ff34253c142b7b88d514efcb Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Thu, 23 Jan 2025 23:08:46 -0500 Subject: [PATCH 13/14] .of() tested --- isType.mjs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isType.mjs.js b/isType.mjs.js index 8f50db7..4b99f14 100644 --- a/isType.mjs.js +++ b/isType.mjs.js @@ -122,7 +122,7 @@ class IsType extends TypeTest { } of(constructor){ - return this.#valueConstructor && (this.#valueConstructor === constructor || this.#valueConstructor.prototype instanceof constructor); + return !!(this.#valueConstructor && (this.#valueConstructor === constructor || this.#valueConstructor.prototype instanceof constructor)); } all(...propNames){ From de018f8790fe606298edf1ad06933e6995a7f725 Mon Sep 17 00:00:00 2001 From: Andy Harrison Date: Thu, 23 Jan 2025 23:30:45 -0500 Subject: [PATCH 14/14] update readme --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b89c90b..06140f9 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ A robust alternative to JavaScript's built-in type testing. See the [test page](https://wizard04wsu.github.io/javascript-type-testing/test/test.htm) for examples. +***Version 4 is not backwards compatible.*** + --- @@ -31,7 +33,7 @@ Syntax: Enumerable properties of the **is** function are string values of the name of each descriptor. These can be used in the `.all()` and `.any()` methods instead of string literals. -For example, `is(_value_).all("number", "object")` is equivalent to `is(_value_).all(is.number, is.object)`. +For example, is(value).all("number", "object") is equivalent to is(value).all(is.number, is.object). ## Type Names and Related Descriptors @@ -67,9 +69,10 @@ For example, `is(_value_).all("number", "object")` is equivalent to `is(_value_) | nonempty | | not _empty_ | `String`, `Array`, `Map`, or `Set` that is not _empty_ | **date** | yes | | `Date` | **error** | yes | | `Error` -| **function** | yes | | `Function`, `function(){}`, `()=>{}` +| **function** | yes | | `Function` | **promise** | yes | | `Promise` -| **regex** | yes | | `Regex`, `/foo/` +| **regex** | yes | | `Regex` +## Note Note that JavaScript doesn't always treat mathematical expressions of undefined or indeterminate form as you might expect. For example, `1/0` is an undefined form, but JavaScript evaluates it as `Infinity`.