Why doesn't Number.isFinite
have a type guard like number is number
?
This example triggers an error Object is possibly 'undefined'
function inc (n?: number) {
return Number.isFinite(n) ? n + 1 : 1;
}
Check it out on the Playground:
Why doesn't Number.isFinite
have a type guard like number is number
?
This example triggers an error Object is possibly 'undefined'
function inc (n?: number) {
return Number.isFinite(n) ? n + 1 : 1;
}
Check it out on the Playground:
Number.isFinite()
isn't considered a type guard due to its potential for unexpected behavior in certain cases.
For example, Number.isFinite(4)
returns true
because the value is a number, which is expected. However, Number.isFinite("apple")
returns false
because the value is not a number. Here's where it gets tricky - Number.isFinite(Infinity)
returns false
, even though the value is technically a number.
In JavaScript, values like Infinity
, -Infinity
, and NaN
are considered type number
but are not finite. Despite the confusion, this distinction is crucial.
While non-finite values are not usually important, they are still valid for mathematical operations and can have practical uses in certain scenarios.
If Number.isFinite()
were to act as a type guard, unexpected results could occur. Consider the following code snippet:
function atLeast(minValue: string | number, num: number): boolean {
const min: number = Number.isFinite(minValue)
? minValue
: Number(minValue.replace(/\D/g, "")); //convert
return num >= min;
}
Executing this code can lead to errors, such as treating a non-number as a number, due to improper type assertion.
In a practical use case, you might encounter a scenario where inadvertent type narrowing results in unexpected behavior. This is why Number.isFinite()
is not suitable as a type guard, as it can incorrectly define the type of a value.
1 NaN
serves as a placeholder for values that cannot be converted to a number, preventing unintentional conversions. This behavior is essential for maintaining the integrity of mathematical operations in JavaScript.
Due to the ambiguous nature of NaN
, comparisons involving it may yield unexpected results, mirroring comparisons between dissimilar string values.
Before delving deeper into this topic, let's provide some context. At first glance, it appears that the issue at hand revolves around the distinction between two JavaScript functions:
isFinite
This function will take its argument and convert it into a number before proceeding with the operation. On the other hand, we have:
Number.isFinite
This function, in contrast, accepts any type of argument and only returns true
if the input is a finite number, without performing any implicit conversions. In TypeScript, these functions have the following signatures:
function isFinite(number: number): boolean
(method) NumberConstructor.isFinite(number: unknown): boolean
One notable difference is that the parameter type in the second method is unknown
instead of number
. This is due to the fact that in JavaScript, Number.isFinite
will return false
for non-numbers, while isFinite
will coerce non-numeric inputs.
> Number.isFinite(false)
false
> isFinite(false)
true
> Number.isFinite(Symbol(2))
false
> isFinite(Symbol(2))
Uncaught TypeError: Cannot convert a Symbol value to a number at isFinite (<anonymous>)
Given that Number.isFinite
is expected to handle inputs of any type and return false
for non-numeric values, it makes sense for the parameter type in TypeScript to be unknown
. Conversely, for isFinite
which is designed to operate solely on numbers and performs coercion that TypeScript helps us avoid, restricting the parameter type to number
is logical.
However, a valid question arises - why can't Number.isFinite
be used as a type guard, since it only returns true
for numerical inputs? The reason is that while it may return true
for some numbers, it does not cover all cases. Attempting to create a custom guard:
function numberIsFinite(n: any): n is number {
return Number.isFinite(n)
}
shows expected behavior for certain inputs:
console.log(numberIsFinite(20)) // true
console.log(numberIsFinite(Number.MAX_VALUE)) // true
console.log(numberIsFinite(undefined)) // false
console.log(numberIsFinite("still ok")) // false
However, when dealing with values such as Infinity
and NaN
:
console.log(numberIsFinite(Infinity)) // false but uh-oh
console.log(numberIsFinite(NaN)) // false but uh-oh
The type guard fails in these scenarios, resulting in unexpected outcomes. This highlights the limitations of using Number.isFinite
as a reliable type guard. If there existed a TS type specifically for "finite numbers," then considering Number.isFinite
as a type guard might make more sense.
I'd like to acknowledge @VLAZ for laying the groundwork for this explanation in their answer. I have expanded upon their insights in this revised response.
In TypeScript, there is no direct way to handle returning a value while also checking for specific conditions. One approach is to include an extra validation step to ensure non-undefined values are considered (potentially removing the need for Number.isFinite in some cases).
function increment (num?: number) {
return typeof num === "number" && Number.isFinite(num) ? num + 1 : 1;
}
Alternatively, you can create your custom guard function for checking finite numbers.
My current project involves making calls to various APIs using JQuery and caching the response from each API. This cached data is then used multiple times on the page to create different dashboard widgets. The issue I'm facing is that if an API retur ...
I've been struggling to find a solution for maintaining the scroll position of my page after a JQUERY toggle event. I've searched extensively but haven't found a fix yet. <script src="Scripts/_hideShowDiv/jquery-1.3.2.min.js" type="text/ ...
I am facing an issue where I set a cookie on one page and try to unset it on the next page that I reach via a link. However, the cookie only gets unset when I refresh the second page by pressing F5. To test this scenario, I have added a link and some cook ...
I have a challenge where I need to send multiple API requests to an endpoint by iterating over an array of values To handle this, I decided to use rxjs library and specifically the forkJoin method //array to keep observables propOb: Observable<any>[ ...
This specific section contains an image positioned at the top, followed by two other components (within <ItemsContainer/>) at the bottom which display as separate rows. I am trying to place these two items (a grid and a chart) side by side within the ...
I have created a custom toggle button component in Angular that I can reuse in different parts of my application. However, I am facing an issue that I need help resolving. I want to display only the selected toggle button and hide the others. When I click ...
When adding a new comment to the list using PHP variables containing HTML code, the method of choice is typically through JQuery. The question arises - what is the most effective way to achieve this? Would one generally opt for an ajax call? Here's t ...
In my index.cshtml view, I have a script that triggers an AJAX call when the SearchingManagerId2 element is changed. $("#SearchingManagerId2").on("change", function () { var valueForSearch = $(this).val(); $.ajax({ ...
Unable to successfully download installers. Encountered an error: TypeError: 'process.env' can only accept a configurable, writable, and enumerable data descriptor. I attempted to resolve this issue by running the command npm install --global wi ...
In the process of developing a React web application, I am focusing on allowing users to register using their email and password. Once registered, each user will have access to their profile information. I have outlined my Firebase data structure below: ...
I recently created a jquery slideshow using a tutorial I found at this link: While the slideshow is functioning correctly for the most part, there is a strange issue that occurs right at the beginning when displaying the first image. Initially, the first ...
Currently in the process of migrating my unit test cases from Jest and Enzyme to React Testing Library. I am working with Material UI's Select component and need to trigger the mouseDown event on the corresponding div to open the dropdown. In my previ ...
I am attempting to extract all the attributes of an element and display their names and values. For instance, an img tag may have multiple unknown attributes that I need to access. My initial idea is as follows: $(this).attr().each(function(index, element ...
I want to change the name and ID of an element when a radio button is clicked. To avoid duplication of the selector, I tried setting it up this way: $( "#selectOther" ).click(function() { $( "[field=primaryInput]" ).attr('id', "modifiedId", ...
I'm currently working on creating a button using JavaScript that, when clicked, will trigger an AJAX request to some PHP code. Interestingly, I have already set up three buttons with the same functionality and they are all functioning perfectly. Wha ...
I am currently experimenting with the following: Stage 1: Making the element draggable from li to div upon dropping into #canvas Placing the draggable element inside #canvas Stage 2: Converting the draggable element from div back to li when dropped i ...
After making a call to the authentication API, the plan is to save the authentication token in LocalStorage and then redirect to a dashboard that requires token validation for entry. However, an issue arises where the authentication token isn't immedi ...
Can a check be implemented for the following scenario? A continuous loop of numbers, such as 1 2 3 4, is being sent to the server. However, I only want each number to be accepted once. Here is my current approach. I believe I am missing one additional c ...
Within my app, I designed a TrackerReact container named ProfileSettingsContainer. This container retrieves the user data with Meteor.user() and passes it to a function called user(), then sends this information to the ProfileSettings component. The main o ...
I have successfully implemented a button to hide and unhide a lengthy menu with a click, but I am struggling to make the menu state persistent across multiple pages. I would like the last toggle action by the visitor to be remembered. Any suggestions on ho ...