lch
lch

Reputation: 4931

Typescript: isNaN not accepting Date Object

const isObjectClass = (value: any, className: any) => (Object.prototype.toString.call(value) === `[object ${className}]`);

export const isDate = (value: any) => isObjectClass(value, 'Date');


const time = (time: Date): string => {
    return ((isDate(time) && !isNaN(time)) ? time : new Date()).toISOString();
};

In the above function time, I am checking if the input is a valid Date object or not. If valid, it will return the string version of passed Date. If invalid, it will return the string version of the current date.

The problem with isDate() is, it will return true for input values new Date() and new Date(undefined)). To avoid this I used !isNaN() which return false in case of !isNaN( new Date(undefined) ).

Now the problem is typescript is not allowing me to pass Date object to isNaN function.

error

TS2345: Argument of type 'Date' is not assignable to parameter of type 'number'.

Any suggestions?

Upvotes: 8

Views: 4238

Answers (3)

Zero Trick Pony
Zero Trick Pony

Reputation: 606

Fixing this by adding a call to getTime() is valid. But I'd point out that your code is already correct as-is, and doesn't need runtime behavior changes to appease Typescript. If you'd like to fix the type-checking without a runtime impact, here are two "pure typescript annotation" fixes.

Why your code isn't wrong

As of this writing, the type library lib.es5.d.ts defines isNaN's parameter as strictly a number, with no other options:

declare function isNaN(number: number): boolean;

...however the specification for isNaN says:

Returns true if the argument coerces to NaN. If ToNumber(number) is NaN, return true.

...so it's correct to pass any type of parameter that can be coerced to a number, it doesn't have to already be a number. The type declaration doesn't model this conversion behavior, even though a Date will indeed coerce to a number. Here are two ways to fix this purely with type annotation:

Option A: Re-declare isNaN()

Since you know that isNaN is more capable and permissive than Typescript declares, you can add your own more permissive declaration to the top of your code. This will override the built-in one in scope, and then your code will typecheck:

declare function isNaN(arg: number|Date): boolean;

Option B: Coercion

For a spot-fix only at this one call site, you can coerce the type of the argument through unknown and then to number, like so:

const time = (time: Date): string => {
  return ((isDate(time) && !isNaN(time as unknown as number)) ? time : new Date()).toISOString();
};

...this is a little less correct because it's not technically true that time is a number, but it will silence the error. Either of these changes will affect only how tsc checks your code, but will not change the generated Javascript.

Upvotes: 3

Kural
Kural

Reputation: 212

You can convert this date object to time (!isNaN(time.getTime())) like as follows,

const time = (time: Date): string => {
return ((isDate(time) && !isNaN(time.getTime())) ? time : new Date()).toISOString();
};

In here getTime method returns numeric value of the respective date object. So you can easily use this numeric value with isNaN

Upvotes: 6

FrV
FrV

Reputation: 258

That's maybe because you want to pass a number in your function time(), number can't be cast as Date.

isObjectClass and isDate are a bit useless, time already check the type of the argument.

Upvotes: -2

Related Questions