doberkofler
doberkofler

Reputation: 10341

Incompatible object types

/* @flow */

type optionsType = Array<{id: string | number, name: string}>;
type modelsType = Array<{id: number, name: string}>;

function getOptions(options: optionsType): string {
  return options.reduce((a, e) => {
    return a + `<option value="${e.id.toString()}">${e.name}</option>`;
  }, '');
}

const options: modelsType = [
  {id: 1, name: 'punto'},
  {id: 2, name: 'duo'},
  {id: 500, name: 'cinquecento'},
];
console.log(getOptions(options));

The above example complains Cannot call "getOptions" with "options" bound to "options" because number [1] is incompatible with string [2] in property "id" of array element. but in my understanding the modelsType is just more generic than the optionsType. Why does flow complain and how can I get this to work as expected?

Upvotes: 1

Views: 1839

Answers (2)

doberkofler
doberkofler

Reputation: 10341

The final solution to my use case:

/* @flow */

type optionsType = $ReadOnlyArray<{+id: string | number, name: string}>;
type modelsType = Array<{id: number, name: string}>;

function getOptions(options: optionsType): string {
  return options.reduce((a, e) => {
    return a + `<option value="${e.id.toString()}">${e.name}</option>`;
  }, '');
}

const options: modelsType = [
  {id: 1, name: 'punto'},
  {id: 2, name: 'duo'},
  {id: 500, name: 'cinquecento'},
];
console.log(getOptions(options));

Upvotes: 0

loganfsmyth
loganfsmyth

Reputation: 161457

If

let second: secondType = first;

were allowed as-is, it would mean that it's valid to do

second.id = "some-id";

but that would corrupt the type of firstType since it's the same object, and the type is number, but it's now been assigned a string.

To make this work, you need to say that secondType.id is read-only, or "covariant". You can do this by changing

type secondType = {id: string | number, name: string};

to

type secondType = {+id: string | number, name: string};

(Example on flow.org/try)

Upvotes: 2

Related Questions