Skip to content

Instantly share code, notes, and snippets.

@rpivo
Last active December 9, 2020 00:39
Show Gist options
  • Save rpivo/a9de0e2236783624adb454c43bb38edc to your computer and use it in GitHub Desktop.
Save rpivo/a9de0e2236783624adb454c43bb38edc to your computer and use it in GitHub Desktop.
Comparing Never & Unknown Types in Flow & TypeScript

Comparing Never & Unknown Types in Flow & TypeScript

Although not fully documented, Flow has a type called empty that is somewhat similar to TypeScript's never type.

In TypeScript, we can use never to indicate a return type that will never happen, or to indicate an otherwise impossible branch of flow.

function neverReturns(): never {
  while (true) {}
}

Similarly, we can use the empty type in Flow, although it seems to behave in unexpected ways. In the below example, Flow thinks that the function will return undefined, which is not really true.

function neverReturns(): empty { // error: Cannot expect empty as the return type of function because empty [1] is incompatible with implicitly-returned undefined.
  while (true) {}
}

We can't use empty to type impossible return types, or at least not for the example above. This will work if we throw an error in the function, though, which would indicate that the function does not return.

function neverReturns(): empty {
  throw new Error()
}

Unfortunately, though, we can give the return value any type here, and Flow won't complain, which is odd:

function neverReturns(): number { // this shouldn't work, but it does
  throw new Error()
}

The empty type also seems to work somewhat like TypeScript's unknown type, which is a type that simply asserts a particular value's type to be unknown. Note, though, that Flow has a type called mixed that is specifically designed for this purpose, and empty shouldn't be used here.

In the below example, we pass in two arguments to checkingUnknown: a, which is a number, and b, which is of type unknown. We also assert that checkingUnknown will return a value that is of type unknown.

Even though, we've cast a as a number as it enters the function, we can create some flow control that checks if a is a number. If it is a number, then we return b, which we've asserted is unknown.

If it isn't a number, we return it, and at that point, we really don't know what type a is. Therefore, it's unknown.

function checkingUnknown(a: number, b: unknown): unknown {
  if (typeof a === 'number') return b
  else return a
}

We can make this same kind of assertion in Flow with empty:

function checkingEmpty(a: number, b: empty): empty {
  if (typeof a === 'number') return b
  else return a
}

But really, this is what Flow's mixed type is for -- to assert a type to be unknown, and also not to completely opt out of type-checking (which is what any would do).

function checkingMixed(a: number, b: mixed): mixed {
  if (typeof a === 'number') return b
  else return a
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment