JSMonk / hegel

An advanced static type checker
https://hegel.js.org
MIT License
2.1k stars 59 forks source link

the "is" keyword (type predicates) #227

Open trusktr opened 4 years ago

trusktr commented 4 years ago

To allow for type refinement to be abstracted into functions:

function isNumber(n: unknown): n is number {
  if (typeof n === 'number' && !isNaN(n)) return true
  return false
}

Try

trusktr commented 4 years ago

Perhaps the following would have a type error:

function isNumber(n: unknown): n is number {
  if (typeof n === 'number') return true // Error, function returns true if n is `number | NaN`
  return false
}

because the type system can treat NaN as not a number (hehe), and throw an error to prevent (possibly silent) runtime errors from NaN being passed around.

trusktr commented 4 years ago

Seems like a lot of opportunity here for Hegel to prevent a lot of errors that TS doesn't!

peter-leonov commented 4 years ago

That would be super sweet to make NaN a separate type and exclude it from number, but making it strictly true requires checking results of all the arithmetical operations 😥.

JSMonk commented 4 years ago

Seems like a lot of opportunity here for Hegel to prevent a lot of errors that TS doesn't!

We want to make it implicity. You can't set directly which type you want to refine. So, your example will look like this:

function isNumber(n: unknown) {
  return typeof n === 'number' && !isNaN(n)
}
const a: number | boolean = 2;
if (isNumber(a)) {
 // a will be number
}

But the feature currently in development. So, I will note this issue in commits related to the feature.

thecotne commented 4 years ago

@JSMonk you should allow developer to specify what he/she wants to refine to so refinement itself can be checked against expectation of a developer

for example

function isNumber<T>(n: T): $Refine<T, number>  {
  return typeof n === 'string'// error
}

function isString<T>(n: T): $Refine<T, string> {
  return typeof n === 'string'// ok
}

declare class Array {
  static isArray<T>(T): $Refine<T, Array>
}

of course inference of $Refine would be super cool but developer needs to be able to specify intent

trusktr commented 4 years ago

That's a good point. It could be possible to make a mistake in the return statement, and therefore by accident change the type guard that the boolean represents.