Reputation: 7442
Flow code can be run here.
Using flow, I have a function that takes a key value pair object and gets a value for it - the value it gets should be a string, number or boolean.
type ValueType = string | number | bool | null | void;
type ObjectOfValues = {[string]: ValueType}
function getValueFromObjectOfValues(objectOfValues: ObjectOfValues, name: string): ValueType {
return objectOfValues[name];
}
I define some object type that has a property that's a maybe
string:
type SomeValueWithNullableString = {
someProperty: ?string
}
Then I create a function that takes my specific object type and calls the function to get a value from it:
function getValue (someObject: SomeValueWithNullableString) {
return getValueFromObjectOfValues(someObject, 'someProperty');
}
This results in a flow error:
type ObjectOfValues = {[string]: ValueType} ^ boolean. This type is incompatible with the expected param type of someProperty: ?string ^ string 2: type ObjectOfValues = {[string]: ValueType} ^ number. This type is incompatible with the expected param type of 9: someProperty: ?string ^ string
What am I doing wrong?
Upvotes: 0
Views: 1245
Reputation: 4086
The problem with this code is that the objects are mutable, so getValueFromObjectOfValues
could legally do objectOfValues.someProperty = 5
.
If Flow allowed this subtyping relationship, then the original caller, who thought they had an object where someProperty
had type ?string
, would now have an object where someProperty
had type number
, thereby breaking the type system.
To solve this problem, you can use property variance. You need to change your type like this:
type ObjectOfValues = {+[string]: ValueType}
This means, loosely, that if you have an object of type ObjectOfValues
, all you know is that its properties are some subtype of ValueType
. This means that when you read from them, you will get a ValueType
. But Flow won't let you write to them, since it doesn't know what type they actually are -- just that they are a subtype of ValueType
.
Upvotes: 2