sindresorhus / is

Type check values

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

is.numericString type guarding returns `x is string` and messes up typescript

Xananax opened this issue · comments

Use case:

const parseValue = (/**@type {string} */value) => {
  if (is.numericString(value)) {
    return parseFloat(value);
  }
  if (value.toLowerCase() === "true" || value.toLowerCase() === "false") {
    return value.toLowerCase() === "true";
  }
  //...
}

Typescript assumes value is a string and then casts value to never:
image

I'm not sure if this is solvable, because strictly speaking the type guard is accurate.

Workaround (for anyone who needs it): recast the value manually:

JS:

value = /**@type {string}*/(value)

TS:

value = value as string

Notably, this recasting is possible in an early return context like my example, but not in an if...else or switch context, so it is still a bit of a bother.

The recasting is also surprising and requires a comment for other teammates that may come across it.

Oh that sounds pretty good and seems like an easy change. Want me to PR this?

commented

Use case:

const parseValue = (/**@type {string} */value) => {
  if (is.numericString(value)) {
    return parseFloat(value);
  }
  if (value.toLowerCase() === "true" || value.toLowerCase() === "false") {
    return value.toLowerCase() === "true";
  }
  //...
}

Typescript assumes value is a string and then casts value to never: image

I'm not sure if this is solvable, because strictly speaking the type guard is accurate.

Workaround (for anyone who needs it): recast the value manually:

JS:

value = /**@type {string}*/(value)

TS:

value = value as string

Notably, this recasting is possible in an early return context like my example, but not in an if...else or switch context, so it is still a bit of a bother.

The recasting is also surprising and requires a comment for other teammates that may come across it.

Hi!

The problem is that in a predicate function, the type guard must always be accurate: if the predicate on a given value returns true, the value must be of the given type in the type guard AND if the predicate returns false, the value must not be of the given type in the type guard.
And this is not the case in the is.numericString predicate. So the solution is indeed to modify the type guard to use a type representing all the numeric string values.

I confirm that changing the signature of numericString like so:

var numericString: (value: unknown) => value is `${number}`;

fixes the problem.