Skip to main content


The empty type has no values. It is the subtype of all other types (i.e. the bottom type). In this way it is the opposite of mixed, which is the supertype of all types.

It is not common to annotate your code using empty. However, there are a couple of situations that it might be useful:

If you have a function that always throws, you can annotate the return as empty, as the function never returns:

1function throwIt(msg: string): empty {2  throw new Error(msg);3}

You can use a cast to empty to assert that you have refined away all members of a union:

1function f(x: 'a' | 'b'): number {2  switch (x) {3    case 'a':4      return 1;5    case 'b':6      return 2;7    default:8      return (x: empty);9  }10}

If you had not checked for all members of the union (for example, changed x to be of type 'a' | 'b' | 'c'), then x would no longer be empty in the default, and Flow would error.

Note: If you want exhaustively checked enums by defualt, without having to cast to empty, you could enable and use Flow Enums in your project.

Since empty is the subtype of all types, all operations are permitted on something that has the empty type. However since no values can be empty, this is "safe", unlike with any.

1const str = "hello";2
3if (typeof str === "string") {4  (str: string); // Yes it's a string5} else {6  // Works! Since we will never enter this branch7  (str: empty);8  const n: number = str + 1;9}

We put "safe" in quotes above, as due type safety holes in your code or bugs within Flow itself, it is possible to get values which are empty typed.

You can use the coverage command to identify code typed as empty.