Mixing the mayBe types with Optional props of an object literal
type Response = {
data?: string;
}
function length(): ?string {
return undefined;
}
function test(): Response {
const data = length()
return {
data
}
}
12: data ^ Cannot return object literal because null or undefined [1] is incompatible with string [2] in property `data`.
References:
5: function length(): ?string {
^ [1]
2: data?: string;
^ [2]
Following the flow documentation for MayBe types and Object Types I am not sure why I get the error, is there a way to fix this ?
Maybe types and optional object properties are not quite the same thing in flow, and are not always compatible.
First let's look at your doc link for Maybe Types:
Maybe types accept the provided type as well as
null
orundefined
. So?number
would meannumber
,null
, orundefined
.
So ?number
is basically number | null | undefined
.
Now let's look at your Object Types link from the docs:
In addition to their set value type, these optional properties can either be
void
or omitted altogether. However, they cannot benull
.
So if we do type Response { data?: string }
then response.data
is basically string | void
. Note that void
is a totally separate type from null
.
So now let's break down your example:
type Response = {
data?: string; // void | string
}
function length(): ?string { // void | string | null
return undefined;
}
function test(): Response {
const data = length()
// at this point, as far as flow knows, `data` can be `void`, `string`, or
// `null`, and we're trying to set it as the `data` property on our
// `Response` type which expects a `void` or a `string`, and does not
// expect a `null`.
return {
data
}
}
So basically, Response.data
is expecting void | string
and you're trying to set it with a void | string | null
. A type must change somewhere in order for you to complete the operation successfully. There are multiple possible approaches:
length
return value to be more specific. Probably the simplest:function length(): void | string {
return undefined;
}
We've removed the possibility of data
being null
, so no more type error.
Response.data
a maybe typeIf they're both maybe types, the problem goes away. The smallest possible change would be this one:
type Response = {
data?: string | null;
}
We've just added the possibility of Response.data
being null
. Now it can accept the return type of length
, types match, no error. But this is a bit confusing, combining the null
with the optional property. We could just do this instead:
type Response = {
data: ?string,
}
function test(): Response {
const data = length()
// at this point response.data is `void`
const response = {};
if (data) {
// data is now a `number`, not `number | void | null`
response.data = data;
}
// response.data is now `number | void` (not `null`)
return response
}
The decision of which of these options to use depends almost entirely on the best API for whatever the problem at hand is, and also possibly stylistic choice on how to approach optional vs maybe types in general.