Typescript: Divide by zero Typechecking

Created on 15 Feb 2018  路  14Comments  路  Source: microsoft/TypeScript

We have type literals for numbers already and a strictNullCheck compiler option -- wouldn't it be awesome if we had a type guard again divide-by-zero errors? I'm building a game that involved a lot of math, computing intersections of lines and such. But what if a line is parallel? It would be cool if the compiler would suggest this as a type error.

function f(): number | 0 {
    return 0
}

const y = 100 / f()


TypeScript Version: 2.7.0-dev.201xxxxx

In Discussion Suggestion

Most helpful comment

You can actually kinda already define an Infinity numeric literal type. Just make a type alias to a number that's too big for JS to accurately represent and it'll round to Infinity. The issue is that the global Infinity constant isn't of this type 馃槈 , and declaration emit for this type may not work 馃槷 . It's super hacky. We should _really_ just add the NaN and Infinity numeric literal types (back). 馃惐 Plus, NaN even has wonky comparability we should encode 馃槃

All 14 comments

Just to weigh in: given that number already implicitly includes 0, it seems

  1. Kind of weird to say that number | 0 would be meaningfully different from 0.
  2. Like kind of a weird responsibility to place on the user to remember to say "hey this number could be zero" when so could every other number.

Since this produces Infinity (which is meaningful) instead of NaN it's hard to even interpret it as an error.
intersectDistance(ray1, ray2) returns Infinity when they're parallel... not a problem?

As Daniel said, this only make sense for numbers known at compile time.
With return types, T extends 0 ? Infinity : number could make sense.

Idris, famous for dependent types like this, uses safe_div : (x : Int) -> (y : Int) -> {auto p : so (y /= 0)} -> Int.

Given an equivalent to Flow's $Call (I'm gonna use regular function syntax for this), I'd imagine a TS equivalent to be implemented like declare function div<B extends number, NotZero = { (v: '1') => 'whatever'; }(B extends 0 ? '0' : '1')>(a: number, b: B).

To break that down, I'm catching the literal type of the denominator in a generic (B), checking whether it is 0, then plugging the answer to that into a partial function. When it turns out the answer does not match what we wanted, the requirements of the partial function are not satisfied, yielding a type error.

(I experimented with this idea in my PR for that feature (#17961), although my implementation ended up too glitchy, relying on synthetic type nodes that resulted in error tooltips showing up in the wrong place.)

Wouldn't something like this work if Infinity was a type?

declare function div(x: number, y: 0): Infinity
declare function div(x: number, y: number): number

yes it would. :)

Interesting. It appears nothing actually breaks until something needs and actual number. It would be cool if Infinity and NaN could be considered separate types.

You can actually kinda already define an Infinity numeric literal type. Just make a type alias to a number that's too big for JS to accurately represent and it'll round to Infinity. The issue is that the global Infinity constant isn't of this type 馃槈 , and declaration emit for this type may not work 馃槷 . It's super hacky. We should _really_ just add the NaN and Infinity numeric literal types (back). 馃惐 Plus, NaN even has wonky comparability we should encode 馃槃

on second thought, I fear my partial function idea might well blow up as soon as it realizes you're potentially plugging in more than it could handle. hm.

it seems @jcalz pulled off something like this at https://github.com/Microsoft/TypeScript/pull/21316#issuecomment-372781934 to disallow certain input

I'd really love to see Infinity and NaN types in TS.
It's always bothered me how they're just... Missing.

And adding a special case for the following doesn't seem too bad,

declare const x : any;
if (x === NaN) {
    //x is now of type `never`
}

@AnyhowStep reminder that there are many NaNs in IEEE 754 floats, and JS adds its own layer of bizarreness on top:

js> var a = 0/0;
NaN
js> var b = {} - 1;
NaN
js> a == b;
false
js> a === b;
false
js> isNaN(a);
true
js> isNaN(b);
true
js> NaN == NaN;
false

It raises the potential question of whether to lump them together.

Could we define real, a subset of number which excludes Infinity or nan?

Then the type of division between two real values would be number, but addition / subtraction / multiplication would still be real.

By your definition, 1e308 is a real.

But their multiplication,

1e308*1e308
> Infinity

And their addition,

> Infinity

And subtraction,

> -Infinity

Oh yeah, we've only got floats :(

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DanielRosenwasser picture DanielRosenwasser  路  3Comments

MartynasZilinskas picture MartynasZilinskas  路  3Comments

blendsdk picture blendsdk  路  3Comments

Antony-Jones picture Antony-Jones  路  3Comments

manekinekko picture manekinekko  路  3Comments