Permalink
Browse files

Expose EmptyT as type annotation `empty`

Summary:
A few people on GitHub have come up with hacky ways to mimic this type, like
`string & number`, but they aren't quite the same.

I think the utility here is pretty marginal, but I'm confident that exposing
this type is at least neutral and should not have any negative effects.

Until reachability lands, `empty` can verify that Flow is convinced of
exhaustiveness.

```
function exhaust(x: number | string) {
  if (typeof x === "number") {
    // do stuff
  } else if (typeof x === "string") {
    // do different stuff
  } else {
    // only true if we handled all cases
    (x: empty);
  }
}
```

I considered that `empty` might confound/confuse poly instantiation, for example
by satisfying a generic type in a way that breaks parametricity.

Well, kind of, but you need to use `declare var`

```
function f<T>(): T {
  declare var x: empty;
  return x;
}
```

and of course the same trick would work with `declare var x: T`, so I think
we're OK.

Without `declare var`, we can devise a function that passes local inference, but
is uncallable.

```
function f<T>(x: empty): T {
  return x;
}
f(); // nothing to pass...
```

Reviewed By: avikchaudhuri

Differential Revision: D3979445

fbshipit-source-id: 911d70dbf7159968d70e7c56d55b9aba4df41e71
  • Loading branch information...
samwgoldman authored and Facebook Github Bot committed Oct 6, 2016
1 parent 0223ff2 commit c603505583993aa953904005f91c350f4b65d6bd
View
@@ -346,7 +346,7 @@ let rec string_of_desc = function
| RString -> "string"
| RBoolean -> "boolean"
| RMixed -> "mixed"
| REmpty -> ""
| REmpty -> "empty"
| RAny -> "any"
| RVoid -> "undefined"
| RNull -> "null"
@@ -919,6 +919,7 @@ end with type t = Impl.t) = struct
match t with
| Any -> any_type loc
| Mixed -> mixed_type loc
| Empty -> empty_type loc
| Void -> void_type loc
| Null -> null_type loc
| Number -> number_type loc
@@ -943,6 +944,8 @@ end with type t = Impl.t) = struct
and mixed_type loc = node "MixedTypeAnnotation" loc [||]
and empty_type loc = node "EmptyTypeAnnotation" loc [||]
and void_type loc = node "VoidTypeAnnotation" loc [||]
and null_type loc = node "NullTypeAnnotation" loc [||]
@@ -136,6 +136,7 @@ module Token = struct
(* Type primitives *)
| T_ANY_TYPE
| T_MIXED_TYPE
| T_EMPTY_TYPE
| T_BOOLEAN_TYPE
| T_NUMBER_TYPE
| T_NUMBER_SINGLETON_TYPE of number_type * float
@@ -277,6 +278,7 @@ module Token = struct
(* Type primitives *)
| T_ANY_TYPE -> "T_ANY_TYPE"
| T_MIXED_TYPE -> "T_MIXED_TYPE"
| T_EMPTY_TYPE -> "T_EMPTY_TYPE"
| T_BOOLEAN_TYPE -> "T_BOOLEAN_TYPE"
| T_NUMBER_TYPE -> "T_NUMBER_TYPE"
| T_NUMBER_SINGLETON_TYPE _ -> "T_NUMBER_SINGLETON_TYPE"
@@ -728,6 +730,7 @@ end
"typeof", T_TYPEOF;
"any", T_ANY_TYPE;
"mixed", T_MIXED_TYPE;
"empty", T_EMPTY_TYPE;
"bool", T_BOOLEAN_TYPE;
"boolean", T_BOOLEAN_TYPE;
"true", T_TRUE;
@@ -260,6 +260,7 @@ end = struct
and primitive = function
| T_ANY_TYPE -> Some Type.Any
| T_MIXED_TYPE -> Some Type.Mixed
| T_EMPTY_TYPE -> Some Type.Empty
| T_BOOLEAN_TYPE -> Some Type.Boolean
| T_NUMBER_TYPE -> Some Type.Number
| T_STRING_TYPE -> Some Type.String
@@ -152,6 +152,7 @@ and Type : sig
and t' =
| Any
| Mixed
| Empty
| Void
| Null
| Number
@@ -55,6 +55,8 @@ let rec convert cx tparams_map = Ast.Type.(function
| loc, Mixed -> MixedT.at loc
| loc, Empty -> EmptyT.at loc
| loc, Void -> VoidT.at loc
| loc, Null -> NullT.at loc
@@ -225,7 +225,7 @@
"end":18
},
{
"type":"(x: ) => ",
"type":"(x: empty) => empty",
"reasons":[],
"loc":{
"source":"test.js",
@@ -240,7 +240,7 @@
"end":20
},
{
"type":"",
"type":"empty",
"reasons":[],
"loc":{
"source":"test.js",
@@ -255,7 +255,7 @@
"end":22
},
{
"type":"",
"type":"empty",
"reasons":[],
"loc":{
"source":"test.js",
@@ -555,7 +555,7 @@
"end":50
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -569,7 +569,7 @@
"end":50
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -583,7 +583,7 @@
"end":50
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -597,7 +597,7 @@
"end":50
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -754,7 +754,7 @@
"end":34
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -1838,8 +1838,8 @@
"end":18
},
{
"raw_type":"{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"function\"\n },\n \"kind\":\"FunT\",\n \"static\":{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"statics of function\"\n },\n \"kind\":\"AnyFunT\"\n },\n \"prototype\":{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"empty prototype object\"\n },\n \"kind\":\"MixedT\"\n },\n \"funType\":{\n \"thisType\":{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"global object\"\n },\n \"kind\":\"MixedT\"\n },\n \"paramTypes\":[\n {\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"\"\n },\n \"kind\":\"EmptyT\"\n }\n ],\n \"paramNames\":[\"x\"],\n \"returnType\":{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"\"\n },\n \"kind\":\"EmptyT\"\n },\n \"isPredicate\":false,\n \"closureIndex\":0,\n \"changeset\":{\"vars\":[],\"refis\":[]}\n }\n}",
"type":"(x: ) => ",
"raw_type":"{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"function\"\n },\n \"kind\":\"FunT\",\n \"static\":{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"statics of function\"\n },\n \"kind\":\"AnyFunT\"\n },\n \"prototype\":{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"empty prototype object\"\n },\n \"kind\":\"MixedT\"\n },\n \"funType\":{\n \"thisType\":{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"global object\"\n },\n \"kind\":\"MixedT\"\n },\n \"paramTypes\":[\n {\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"empty\"\n },\n \"kind\":\"EmptyT\"\n }\n ],\n \"paramNames\":[\"x\"],\n \"returnType\":{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"empty\"\n },\n \"kind\":\"EmptyT\"\n },\n \"isPredicate\":false,\n \"closureIndex\":0,\n \"changeset\":{\"vars\":[],\"refis\":[]}\n }\n}",
"type":"(x: empty) => empty",
"reasons":[],
"loc":{
"source":"test.js",
@@ -1854,8 +1854,8 @@
"end":20
},
{
"raw_type":"{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"\"\n },\n \"kind\":\"EmptyT\"\n}",
"type":"",
"raw_type":"{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"empty\"\n },\n \"kind\":\"EmptyT\"\n}",
"type":"empty",
"reasons":[],
"loc":{
"source":"test.js",
@@ -1870,8 +1870,8 @@
"end":22
},
{
"raw_type":"{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"\"\n },\n \"kind\":\"EmptyT\"\n}",
"type":"",
"raw_type":"{\n \"reason\":{\n \"pos\":{\n \"source\":null,\n \"type\":null,\n \"start\":{\"line\":0,\"column\":1,\"offset\":0},\n \"end\":{\"line\":0,\"column\":0,\"offset\":0}\n },\n \"desc\":\"empty\"\n },\n \"kind\":\"EmptyT\"\n}",
"type":"empty",
"reasons":[],
"loc":{
"source":"test.js",
@@ -2173,7 +2173,7 @@
"end":50
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -2187,7 +2187,7 @@
"end":50
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -2201,7 +2201,7 @@
"end":50
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -2215,7 +2215,7 @@
"end":50
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",
@@ -2374,7 +2374,7 @@
"end":34
},
{
"desc":"",
"desc":"empty",
"loc":{
"source":"test.js",
"type":"SourceFile",

0 comments on commit c603505

Please sign in to comment.