4

-(Edited)This question does not apply to only Numbers, but about all types in general. I am using something like:

exampleFunction1(a,b){
   if(
        Object.prototype.toString.call(a) === "[object Number]" &&
        Object.prototype.toString.call(b) === "[object Number]"
     ){
        return a+b;
     }else{
        console.log('%cType Error : exampleFunction1(<Number>)','color:#00FF66;')
     }
}

exampleFunction2(type,length,format) {
        if(
            Object.prototype.toString.call(type) === "[object String]" &&
            Object.prototype.toString.call(length) === "[object Number]" &&
            (Object.prototype.toString.call(format) === "[object String]" || Object.prototype.toString.call(format) === "[object Undefined]" )
        ){

        }else{
            console.log("%cType Error : exampleFunction2(<String>,<Number>,<String>)","color:#00FF66;")
        }
    }

in almost all of my functions to strict check its input type before starting to write my actual code. And its mostly on functions that i'll share amongst my team and on my own library which other people will try to use. Is this considered as a good practice or it's unnecessary ?

3 Answers3

1

You should use isNaN method to check is number or not as

addNumbers(a,b){
   if(
        !isNaN(a) &&
        !isNaN(b)
     ){
        return a+b;
     }else{
        console.log('%cType Error : findUniqueInArray(<Array>)','color:#00FF66;')
     }
}
Hien Nguyen
  • 24,551
  • 7
  • 52
  • 62
1

You won't find one right answer, but you can find strong opinions. My opinion is: it varies. Here are some typechecks from my library

const isDefined = x => x !== undefined && x !== null

const isUndefined = x => x === undefined

const isNull = x => x === null

const isIterable = x => isDefined(x) && isDefined(x[Symbol.iterator])

const isAsyncIterable = x => isDefined(x) && isDefined(x[Symbol.asyncIterator])

const isReadable = x => x && typeof x.read === 'function'

const isWritable = x => x && typeof x.write === 'function'

const isFunction = x => typeof x === 'function'

const isArray = Array.isArray

const numberTypedArrays = new Set([
  'Uint8ClampedArray',
  'Uint8Array', 'Int8Array',
  'Uint16Array', 'Int16Array',
  'Uint32Array', 'Int32Array',
  'Float32Array', 'Float64Array',
])

const isNumberTypedArray = x => x && x.constructor && (
  numberTypedArrays.has(x.constructor.name)
)

const bigIntTypedArrays = new Set([
  'BigUint64Array', 'BigInt64Array',
])

const isBigIntTypedArray = x => x && x.constructor && (
  bigIntTypedArrays.has(x.constructor.name)
)

const isNumber = x => typeof x === 'number'

const isBigInt = x => typeof x === 'bigint'

const isString = x => typeof x === 'string'

const isPromise = x => x && typeof x.then === 'function'

const is = fn => x => x && x.constructor === fn

const isObject = is(Object) // checks directly for Object, isObject([]) === false

If you're looking for a quick way to just check the type given the constructor function, I recommend copy and pasting is and using it so

const is = fn => x => x && x.constructor === fn

exampleFunction1(a,b){
   if(is(Number)(a) && is(Number)(b)){
        return a+b;
     }else{
        console.log('%cType Error : exampleFunction1(<Number>)','color:#00FF66;')
     }
}

it's less to type.

richytong
  • 2,387
  • 1
  • 10
  • 21
  • Thanks for sharing your code. Just a question: wouldn't it be much more easier to define Object.prototype.toString.call() === "[object ]" repeatedly rather than having different logic for each type ? – Tsubaki Cerceis May 26 '20 at 01:21
  • That actually work very well given that is(Object), is(Array), is(Float32Array) etc works just fine. Is there any type this method won't work on ? – Tsubaki Cerceis May 26 '20 at 01:52
  • It unfortunately does not work for `TypedArray` which is hidden from the global namespace. I wanted to do an easy type check for `Uint8Array, Int8Array, ..., Float64Array` as a group, so that's how I arrived at `isNumberTypedArray`. I also ran into a bunch of one off cases like `isDefined` where I want to conveniently check for undefined and null. So i guess that's where all the variation came from for me. – richytong May 26 '20 at 01:57
  • also when you just want to see if something has a certain method, like `isReadable` just checks for the function `read`. this is [duck typing](https://en.wikipedia.org/wiki/Duck_typing) – richytong May 26 '20 at 01:59
  • For the isDefined part. If you entered nothing , is()(input) returns "undefined" won't that be enough to check ? – Tsubaki Cerceis May 26 '20 at 02:05
0

Relying on toString() doesn't seem like the safest choice, as any object can implement its own toString().

I'd go with Number.isFinite():

if (Number.isFinite(a) && Number.isFinite(b)) {

If you just want to check the type, there's a typeof operator:

typeof 1 // "number"
typeof 'a' // "string"
typeof {} // "object"

As for the toString(), libraries often implement their own toString() to help you debug. Here's how it's done with classes:

class Foo {
    toString() {
        return 'I am mr. Foo';
     }
}

const bar = new Foo();
bar.toString() // "I am mr. Foo"
Robo Robok
  • 21,132
  • 17
  • 68
  • 126
  • This question is not only about numbers, but also for other types. Also can you give an example of "as any object can implement its own toString()."? – Tsubaki Cerceis May 26 '20 at 00:56
  • Updated my answer to explain. – Robo Robok May 26 '20 at 01:04
  • typeof is unreliable because typeof Object and typeof Array returns the same result – Tsubaki Cerceis May 26 '20 at 01:09
  • Yup, it's not good for arrays. There's no one liner for all the types. If you wanna be type safe, use TypeScript, as suggested. – Robo Robok May 26 '20 at 01:12
  • 1
    `Object.prototype.toString.call(obj)` never calls `obj.toString` so that is not an issue. – Marco May 26 '20 at 01:13
  • Well, yeah - unless somebody plays with the Object prototype globally you are right. But I don't think anyone should do any speculations relying on `toString()`. – Robo Robok May 26 '20 at 01:16
  • @RoboRobok According to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray given "isArray" as an example, the type check performed internally is actually Object.prototype.toString.call(arg) === '[object Array]'; . Does it means that the check in this is unreliable? – Tsubaki Cerceis May 26 '20 at 01:25
  • They aren't saying it's done like that internally. They suggest such a polyfill, but Mozilla's polyfills are often far from perfect. You know, you can use `toString()` if you really want to, I'm just saying it's a bad practice in my opinion, because it can be anything. But if you check types a lot, you really should check out TypeScript. The code gets quite ugly with type checks inside methods. – Robo Robok May 26 '20 at 01:29
  • Ok i see what you mean there. But i just don't quite see the problem of using "Object.prototype.toString" , in the example given by you. if you console.log Object.prototype.toString(bar) it does not return [object String], it returns [object Object] which actually does it job preventing variables other than String into the function – Tsubaki Cerceis May 26 '20 at 01:44
  • It's one of these situations where you can do it and be safe, but it's not considered a great way to go. I don't think there's any standard of what `toString()` should be. All popular implementations of JS engine will be the same, but in theory they don't need to be. It's up to you how much of an idealist you feel today :) – Robo Robok May 26 '20 at 01:47