30
type someType = {
  keyOne: string,
  keyTwo: string,
};

type someOtherType = {
  keyOne: string,
  keyTwo: string,
  keyThree: string,
};

Both of these types are objects that contain keyOne and keyTwo, the only difference is the latter extends the former with an additional key of keyThree.

Rather than writing duplicated code, is it possible to build the someOtherType flow type by extending someType? In my mind, ES6 object rest/spread comes to mind, but I'm not sure how to accomplish something like this in Flow.

Thanks!

Jon Cursi
  • 2,837
  • 3
  • 19
  • 48
  • 1
    Possible duplicate of [FlowType: Inheritance of Types (Type A is a subset of type B ...)](http://stackoverflow.com/questions/42281539/flowtype-inheritance-of-types-type-a-is-a-subset-of-type-b) – Nat Mote Mar 03 '17 at 17:02
  • Cool, thanks for the link. – Jon Cursi Mar 03 '17 at 18:14

2 Answers2

30

What you're looking for is the intersection type. According to the documentation:

An intersection type requires a value to be all of the input types.

Syntax: Intersection: < type 1 > & < type 2 > ... & < type n >

The intersection type is intended to extend an existing type and add additional type requirements to it.

type someType = {
  keyOne: string,
  keyTwo: string
}

type someOtherType = someType & {
  keyThree: string
}

const shouldBeOk: someOtherType = {
  keyOne: 'biz',
  keyTwo: 'buzz',
  keyThree: 'baz',
}

const shouldError: someOtherType = {
  keyOne: 123,
  keyTwo: 'hello',
  keyThree: 'world',
}

// flow error:
16: const shouldError: someOtherType = {
                               ^ object literal. This type is incompatible with
8: type someOtherType = someType & {
                        ^ object type

The logical opposite of the intersection type is the union type. According to the documentation:

A union type requires for a value to be one of the input types.

Syntax: Union: < type 1 > | < type 2 > ... | < type n >

As an example. you can use the union type to create an enumerable.

type fooBarBazType = 'foo' | 'bar' | 'baz';
const shouldBeOk: fooBarBazType = 'bar';

const shouldError: fooBarBazType = 'buzz';

4: const shouldError: fooBarBazType = 'buzz';
                                      ^ string. This type is incompatible with
4: const shouldError: fooBarBazType = 'buzz';
                      ^ string enum
Community
  • 1
  • 1
thejohnbackes
  • 1,157
  • 10
  • 19
  • 1
    the answer for the "possible duplicate" noted above is out of date. Intersection types [have been implemented since July 1, 2016](https://flowtype.org/blog/2016/07/01/New-Unions-Intersections.html). – thejohnbackes Mar 07 '17 at 00:18
  • 1
    It's not out of date. In my answer in that link, I mention intersection types. They do work for most cases. However they can lead to strange error messages and they don't work as expected in some cases (e.g. with exact types). Object type spread will work better for this purpose and in fact it just landed yesterday: https://github.com/facebook/flow/commit/ad443dc92879ae21705d4c61b942ba2f8ad61e4d – Nat Mote Mar 07 '17 at 14:57
  • sorry Nat, I misunderstood. I thought you were saying that intersection types were not released yet. – thejohnbackes Mar 07 '17 at 17:30
17

Sorry, the accepted answer is just wrong and it's working just because you didn't use exact match.

When using exact match you'll get an error:

10: const shouldBeOk: someOtherType = {
^ Cannot assign object literal to shouldBeOk because property keyOne is missing in object type 1 but exists in object literal 2. References: 6: type someOtherType = someType & {|
^ 1 10: const shouldBeOk: someOtherType = {
^ 2

The right way to do it is to use the spread operation:

type someOtherType = {|
  ...someType,
  keyThree: string
|}

demo

shem
  • 4,368
  • 2
  • 28
  • 42
  • 2
    In addition to spread, there are a number of [Utility Types](https://flow.org/en/docs/types/utilities/) that can help mix/match Type ontologies. +1 for mentioning spread. – deepelement Apr 26 '20 at 19:17