5 ms·
In TypeScript, type guards are considered to be exhaustive, so if you have a string | number and check it against a function that says it returns "x is number",
by RyanCavanaugh 4y ago
In TypeScript, type guards are considered to be exhaustive, so if you have a string | number and check it against a function that says it returns "x is number", then TypeScript will think that in the negative case, it's a string.
If isInteger was marked as a type guard, then you could write code like this:
function f(s: string | number) {
if (!Number.isInteger(s)) {
console.log(s.substring(0, 0));
}
}
which is clearly wrong.
There's an open feature request for a new kind of "one-sided" type guards that don't cause narrowing when they return false.
- benatkin 4y agoThat seems like an important feature request. I see TypeScript adding fancy stuff without fixing core limitations.
- explaininjs 4y agoYou 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.
- benatkin 4y agoThat doesn't seem to address my concern. It seems like more of a curiosity. Whatever floats your boat.
- explaininjs 4y agoHow 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.
- deleted 4y ago[deleted]
- benatkin 4y agoif (typeof s === 'number' && Number.isInteger(s)) {
- explaininjs 4y agoWhat do you expect me to do with this snippet provided with no context?
- benatkin 4y agoThat'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.
- explaininjs 4y agoThe 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.
- deleted 4y ago[deleted]
- benatkin 4y agoIt 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
- slooonz 4y agoHow is it wrong ?
- silasdavis 4y agoIf s is a non-integer number
- deleted 4y ago[deleted]
- HeavyFeather 4y agoI mean, that could be changed. Currently: either string OR number Possible: either string | number OR just number
- deleted 4y ago[deleted]
- yencabulator 4y ago"string | number | number" is just "string | number" to Typescript.
- benatkin 4y agoThe OR doesn't indicate | in TypeScript, it indicates the result of the possible future type guard.
- schwartzworld 4y ago> The OR doesn't indicate | in TypeScript, it indicates the result of the possible future type guard. Are you implying in your other comment (that HN won't let me reply to) that Typescript has an "OR" operator that is distinct from "|"? Can you link to documentation on that?
- HeavyFeather 4y ago