Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flowtype extend object type

As JavaScript developer I'm new to type checking and I struggle to understand why this simple code is not working:

type Animal = {
  id: number,
  name: string,
  type: 'dog' | 'cat'
};

type Dog = {
  id: number,
  name: string,
  type: 'dog',
  color: string
};

function printAnimal(animal: Animal): string {
  return `${animal.type}: ${animal.name}`;
}

const buddy: Dog = {
  id: 1,
  name: 'Buddy',
  type: 'dog',
  color: 'black'
}

printAnimal(buddy);

What I'm trying to achieve here is to have a method that accepts interface. This however gives me error: Cannot call 'printAnimal' with 'buddy' bound to 'animal' because string literal 'dog' [1] is incompatible with string literal 'cat' [2] in property 'type'..

What I tried:

  1. interface Animal { // ...} - does not work.
  2. Remove typing from buddy - it work's but I'm not satisfied. Sometimes I do want to have more strict type (so I know I'm dealing with dog not cat) but still use general method that accept any animal.
  3. I tried changing type: 'dog' | 'cat' to type: string - does not work. I would expect 'dog' string is subtype of general string type but it's not. On the other hand even if it works it wouldn't be enough - sometimes I know that my app accepts only dogs and cats not any other animals.

Thanks for reading and I hope I can get some help from you guys! Here's live version: Try Flow - live example

like image 789
Ernest Nowacki Avatar asked Nov 07 '22 00:11

Ernest Nowacki


1 Answers

You have to make the Animal type an interface since it's describing your types implementations as a "parent". And it would make sense if you enforce it by extending your Dog type via a union since that's the point of using typings to reach a stronger type-checking.

This can be written like this:

/* @flow */

interface Animal {
  id: number,
  name: string,
  type: 'dog' | 'cat'
};

type Dog = Animal & {
  type: 'dog',
  color: string
};

function printAnimal(animal: Animal): string {
  return `${animal.type}: ${animal.name}`;
}

const buddy: Dog = {
  id: 1,
  name: 'Buddy',
  type: 'dog',
  color: 'black'
}

printAnimal(buddy);
like image 176
Ivan Gabriele Avatar answered Nov 13 '22 20:11

Ivan Gabriele