typeof/js subtype test nullish test to-number
--------- ------------ ------------ ---------
undefined undefined (error) (error)
boolean boolean === false +v
number number === 0 v
bigint bigint === 0n Number(v)
strumber string !isNaN(v) && v.trim() == 0 +v
booing string "true", "false" === "false" 0, 1
string string else v.trim() === "" 0 or (error)
array object Array.isArray(v) === [] v.length
date object v instanceof Date isNaN(v) yyyymmddhhmmss.fff
observable object v instanceof Observable (unbox) (unbox)
null object v === null always nullish 0
object object else len == 0 Object.keys(v).length
function function (evaluate) (call)
strange symbol (never) (error)
class Subtype {
static tell(v) {
switch (typeof v) {
//there is no "undefined" subtype - it's an error to check its type
//dude, just initialize your variables before using them, k?
case "undefined": return
case "boolean": return "boolean"
case "number": return "number"
case "bigint": return "bigint"
case "string":
if (!isNaN(v) && v.trim()) {
return "strumber"
}
if (v === "true" || v === "false") {
return "booing"
}
return "string"
case "object":
if(v === null) {
return "null"
}
if(Array.isArray(v)) {
return "array"
}
if (v instanceof Date) {
return "date"
}
if (v instanceof Observable) {
return "observable"
}
return "object"
case "function": return "function"
default: return "strange"
}
}
static isNullish(v) {
return !toNumber(v)
}
static toBoolean(v) {
return !!toNumber(v)
}
static toNumber(v) {
if (v === null || v === "false" || v === "null") {
return 0
}
if (v === "true") {
return 1
}
switch (typeof v) {
case "object":
if (v instanceof Observable) {
//assume Observable does not contain itself as value
return this.toNumber(v.value)
}
if (v instanceof Date) {
if (isNaN(v)) {
return 0
}
// yyyymmddhhmmss.fff representation
let num = v.getUTCFullYear()
num = 100*num + v.getUTCMonth() + 1
num = 100*num + v.getUTCDate()
num = 100*num + v.getUTCHours()
num = 100*num + v.getUTCMinutes()
num = 100*num + v.getUTCSeconds()
return num + v.getUTCMilliseconds()/1000
}
return Object.keys(v).length
case "bigint":
return Number(v)
default:
if (isNaN(v)) {
return v
}
return +v
}
}
static toDate(v) {
const parsed = Date.parse(v)
if (parsed) {
return new Date(parsed)
}
// in this representation, adding 2 hours 00 minutes 00 secs is simple:
// add 20000; don't add or subtract too much at once, e.g. 100 seconds
// cannot be distinguished from 1 minute; precision allows 4-msec steps
let rem = toNumber(v)
const ms = rem % 1 * 1000
const sec = (rem + 20) % 100 - 20 //force it between -20 and 79 (add/sub up to 20 sec)
rem = Math.floor(rem/100)
const mi = (rem + 20) % 100 - 20 //add/sub up to 20 min
rem = Math.floor(rem/100)
const hour = (rem + 38) % 100 - 38 //add/sub up to 38 hours
rem = Math.floor(rem/100)
const day = (rem + 35) % 100 - 35 //add/sub up to 35 days
rem = Math.floor(rem/100)
const mon = (rem + 44) % 100 - 45 //add up to 43 months, sub 45 (12+43+44=99)
rem = Math.floor(rem/100)
let d = new Date()
d.setUTCFullYear(rem)
d.setUTCMonth(mon)
d.setUTCDate(day)
d.setUTCHours(hour)
d.setUTCMinutes(mi)
d.setUTCSeconds(sec)
d.setUTCMilliseconds(ms)
return d
}
}