Skip to content

Instantly share code, notes, and snippets.

@kevinrobinson
Last active August 16, 2016 13:12
Show Gist options
  • Save kevinrobinson/37b09ba96f662a5500b68588fc6634f0 to your computer and use it in GitHub Desktop.
Save kevinrobinson/37b09ba96f662a5500b68588fc6634f0 to your computer and use it in GitHub Desktop.
Trying to use disjoint unions in Flow
/* @flow */
// Modeled after https://github.com/facebook/flow/issues/1664#issuecomment-222934455
type AnimalT = {
id: number,
name: string
};
type CatT = AnimalT & {
isPurring: bool
};
type DogT = AnimalT & {
isBarking: bool
};
// This is cool.
const garfield:AnimalT = {
id: 42,
name: 'Garfield',
isPurring: true
};
console.log(garfield);
// This is cool.
const rex:DogT = {
id: 45,
name: 'Rex',
isBarking: false
};
console.log(rex);
// Trying to check for a property and then work with a more
// specific type doesn't seem to work.
if (garfield.isPurring) {
const cat:CatT = garfield;
console.log(cat);
}
// Nor does the approach in https://github.com/facebook/flow/issues/1664#issuecomment-222934455
if (garfield.isPurring) {
(garfield:CatT);
console.log(garfield);
}
@kevinrobinson
Copy link
Author

kevinrobinson commented Aug 16, 2016

Output is:

demo.js:7
  7: type CatT = AnimalT & {
                           ^ property `isPurring`. Property not found in
 34:   const cat:CatT = garfield;
                        ^^^^^^^^ object type

demo.js:7
  7: type CatT = AnimalT & {
                           ^ property `isPurring`. Property not found in
 40:   (garfield:CatT);
        ^^^^^^^^ object type

Using: Flow, a static type checker for JavaScript, version 0.30.0

@kevinrobinson
Copy link
Author

This is what I had initially tried, and what seemed most intuitive:

type AnimalT = CatT | DogT;
type CatT = {
  id: number,
  name: string,
  isPurring: bool
};
type DogT = {
  id: number,
  name: string,
  isBarking: bool
};

It doesn't work either and yields similar errors:

src/message_popup/demo.js:35
 35:   const cat:CatT = garfield;
                 ^^^^ property `isPurring`. Property not found in
 35:   const cat:CatT = garfield;
                        ^^^^^^^^ object type

src/message_popup/demo.js:41
 41:   (garfield:CatT);
                 ^^^^ property `isPurring`. Property not found in
 41:   (garfield:CatT);
        ^^^^^^^^ object type

@kevinrobinson
Copy link
Author

I also tried adding in an explicit sentinel value (versus matching on the fields that are present). I was trying to match the approach in the docs here: https://flowtype.org/docs/disjoint-unions.html

type Result = Done | Error; // a disjoint union type with two cases
type Done = { status: 'done', answer: Matrix };
type Error = { status: 'error', message: string };

This didn't work either, and my read of the error message is that even expressing the disjoint union was a problem (before even getting to checking the real code)

Code:

type CatT = {
  id: number,
  name: string,
  type: 'cat',
  isPurring: bool
};
type DogT = {
  id: number,
  name: string,
  type: 'dog',
  isBarking: bool
};
type AnimalT = CatT | DogT;


// This is cool (intentionally generic type)
const garfield:AnimalT = {
  id: 42,
  name: 'Garfield',
  type: 'cat',
  isPurring: true
};
console.log(garfield);

// This is cool.
const rex:DogT = {
  id: 45,
  name: 'Rex',
  type: 'dog',
  isBarking: false
};
console.log(rex);

// Trying to check for a property and then work with a more
// specific type doesn't seem to work.
if (garfield.type === 'cat') {
  const cat:CatT = garfield;
  console.log(cat);
}

// Nor does the approach in https://github.com/facebook/flow/issues/1664#issuecomment-222934455
if (garfield.isPurring) {
  (garfield:CatT);
  console.log(garfield);
}

This led to these errors:

demo.js:6
  6:   type: 'cat',
             ^^^^^ string literal `cat`. Expected string literal `dog`, got `cat` instead
 12:   type: 'dog',
             ^^^^^ string literal `dog`

demo.js:12
 12:   type: 'dog',
             ^^^^^ string literal `dog`. Expected string literal `cat`, got `dog` instead
  6:   type: 'cat',
             ^^^^^ string literal `cat`

demo.js:45
 45:   (garfield:CatT);
                 ^^^^ property `isPurring`. Property not found in
 45:   (garfield:CatT);
        ^^^^^^^^ object type

@kevinrobinson
Copy link
Author

Interestingly, the switch statement works and also detects the type error:

switch (garfield.type) {
  case 'cat': console.log(garfield.isPurring); break;
  case 'dog': console.log(garfield.isBarking); break;
}

and changing them both to isBarking yields:

 51:   case 'cat': console.log(garfield.isBarking); break;
                                        ^^^^^^^^^ property `isBarking`. Property not found in
 51:   case 'cat': console.log(garfield.isBarking); break;
                               ^^^^^^^^ object type

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment