Menu

Type System

Csaba Skrabák
            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
    }
}

Related

Wiki: Home

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.