TypeScript Version: 2.4.1
Code
var baz = 42;
console.log(parseFloat(baz)); //42
Expected behavior:
No error. Console logs 42
Actual behavior:
error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
also:
Code
var foo = {
toString: function () { return "500"; }
}
console.log(parseFloat(foo)); //500
Expected behavior:
No error. Console logs 500
Actual behavior:
error TS2345: Argument of type '{ toString: () => string; }' is not assignable to parameter of type 'string'
According to the ECMA specs there is not any requirement that value passed to the parseFloat function must be a string. I would say even more, the behavior of this function is well defined for object with have either valueOf or toString functions. In such case correct code in Javascript is not a correct one in Typescript. This error occured when in our project we were rewritting js code to typescript, and such apperance of invoking of parseFloat with number occured. There is an easy workaround for this, however still it is not correct
According to the ECMA specs there is not any requirement that value passed to the parseFloat function must be a string.
https://stackoverflow.com/questions/41750390/what-does-all-legal-javascript-is-legal-typescript-mean
I would say even more, the behavior of this function is well defined for object with have either valueOf or toString functions
Literally every object in JS that wasn't created from Object.create(null) satisfies this requirement. It seems rather wrong to call parseFloat({x: 3}) even though ({x: 3}).toString isn't undefined
such apperance of invoking of parseFloat with number occured
I'm surprised that calling parseFloat on a number isn't considered a problem? At best it's fishy, at worst it means the entirely wrong thing is happening.
Firstly, thanks for the link! It has clarified a lot.
I'm surprised that calling parseFloat on a number isn't considered a problem? At best it's fishy, at worse it means the entirely wrong thing is happening.
100% agreed, however well it 'happened' when we didn't had typescript. We were lucky because still the behavior is well defined for such case. In long run ofc we want to fix occurance of such invoking, because it is fishy. However still, according to the specs the parseFloat is possible to handle such cases, and in TypeScript you are not able to do that.
@RyanCavanaugh the spec says to cast the input parameter to string: http://www.ecma-international.org/ecma-262/6.0/#sec-parsefloat-string
Of course the spec defines the behavior. Math.max says cast the inputs to number; that doesn't mean Math.max(window, ["hello"]) is something TypeScript should allow.
I would disagree a bit. In case with Math.max we have straight situation, any of values is NaN -> return NaN. However in case with parseFloat we don't have such a thing. If the spec something like said: 'If arg is not type of string return NaN' it would be perfectly fine to just assume that the argument can be a string only. Even if the behavior was defined for numbers, it is fine imho to just say, ok this function has no sense for numbers. However we have here defined special behavior if passed object has a valueOf or toString method. So by throwing away this behavior the function itself is very different. All I mean is that if the case was that for all object it is Nan, I am perfectly fine with behavior as it is, but in current form this is more like some utility function 'strictParseFloat' which take a use of some cases of defined parseFloat.
However we have here defined special behavior if passed object has a valueOf or toString method.
Again, all objects have these methods. The coercion via valueOf / toString is how every coercion in the spec works.
Is the line somewhat hazy? Yes. Is the correct rule "allow every coercion" ? Clearly no.
It seems like TS should then require that the function passed to Array.filter return a boolean to be consistent.
@jcready used to work like that #5850
Really just a duplicate of #4002. Everything old is new again
So then why is this "Working as Intended" while that was changed? It seems like a nearly identical use-case, no?
I would argue that for built-ins like parseFloat, isNaN, etc. that the input parameters should be any. The main reason for type checking input parameters for a function is to prevent runtime errors, but in these cases we know that no matter what input you pass to these functions you will not get a runtime error.
@jcready I think a primary reason for type checking is to prevent bugs. Not all bugs manifest as runtime errors.
Most helpful comment
https://stackoverflow.com/questions/41750390/what-does-all-legal-javascript-is-legal-typescript-mean
Literally every object in JS that wasn't created from
Object.create(null)satisfies this requirement. It seems rather wrong to callparseFloat({x: 3})even though({x: 3}).toStringisn'tundefinedI'm surprised that calling
parseFloaton a number isn't considered a problem? At best it's fishy, at worst it means the entirely wrong thing is happening.