Hacker News new | past | comments | ask | show | jobs | submit login

You can get around it pretty easily:

    const intSymbol = Symbol('integer')
    type integer = number & {[intSymbol]: never}

    const isInteger = (n: unknown): n is integer => Number.isInteger(n)

    function f(s: string | number) {
        if (isInteger(s)) {
            const allowed = s.toExponential()
        } else {
            // s still string | number
        } 
    }
With this you even get to define functions that must accept integers, which is kinda neat.



That doesn't seem to address my concern. It seems like more of a curiosity. Whatever floats your boat.


How does it not address your concern? It’s quite literally making isInteger into a type guard. The ‘integer’ type above can passed to any place ‘number’ is needed.


  if (typeof s === 'number' && Number.isInteger(s)) {


What do you expect me to do with this snippet provided with no context?


That's what I was wondering when I saw yours.

What I was showing here is that this solution is simpler than yours and just as good. Rather than add a utility function and a faux primitive type, I just do the normal workaround for TypeScript not supporting this, which is to redundantly check that something is both a number and an integer.


The point of having a utility function is that you don't have to do the redundant checks every time you want to make sure it's both a number and an integer.

The critical bit is that you need to define a new type for `integer`s distinct from `number`s to allow reusing the code in a way that doesn't break the type system on the negative path, as Ryan and I demonstrated.


It isn't redundant in terms of the amount of code written, to me. The overhead of having the utility function and type is greater IMO.

If it's in terms of performance, that seems like moving the goalposts. I also wonder if it could be optimized away.

Next time I run into it I might use this:

  if (Number.isInteger(s)) {
    const allowed = (s as number).toExponential()
...and keep the isInteger check close enough that it's readable.

...or this:

  if (Number.isInteger(s)) {
    const n = s as number // should be optimized away by the compiler I think


You asked for isInteger to work as a type guard and I showed you how. If you prefer explicit casts everywhere that's fine, but it isn't a type guard. You could even use the type anonymously if you really want: (n is number & {Symbol(): never}).

Whatever floats your boat, as you say.


Oh boy, I never ever asked for a replacement for Number.isInteger! I prefer to use native functions.

Have a nice day.


Then alter the type of the ambient native function itself:

    interface NumberConstructor {
      isInteger(n: unknown): n is number & { Symbol(): never }
    }




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: