Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Divide by zero Typechecking #21955

Open
ccorcos opened this issue Feb 14, 2018 · 14 comments
Open

Divide by zero Typechecking #21955

ccorcos opened this issue Feb 14, 2018 · 14 comments
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@ccorcos
Copy link

ccorcos commented Feb 14, 2018

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

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Feb 15, 2018

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.

@DanielRosenwasser DanielRosenwasser added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Feb 15, 2018
@RyanCavanaugh
Copy link
Member

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?

@KiaraGrouwstra
Copy link
Contributor

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.)

@jendrikw
Copy link

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

@KiaraGrouwstra
Copy link
Contributor

yes it would. :)

@ccorcos
Copy link
Author

ccorcos commented Feb 17, 2018

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.

@weswigham
Copy link
Member

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 😄

@KiaraGrouwstra
Copy link
Contributor

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.

@KiaraGrouwstra
Copy link
Contributor

it seems @jcalz pulled off something like this at #21316 (comment) to disallow certain input

@AnyhowStep
Copy link
Contributor

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`
}

@calimeroteknik
Copy link

calimeroteknik commented Oct 30, 2019

@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.

@DanielHeath
Copy link

DanielHeath commented Apr 22, 2020

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.

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Apr 22, 2020

By your definition, 1e308 is a real.

But their multiplication,

1e308*1e308
> Infinity

And their addition,

1e308+1e308
> Infinity

And subtraction,

-1e308-1e308
> -Infinity

@DanielHeath
Copy link

Oh yeah, we've only got floats :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

9 participants