Reputation: 935
I have a flowtype object declaration that looks like this:
type ProjectType = {
// removed for brevity
releases?: Array<ReleaseType>
}
There are times when releases is not included in the object, so when I want to use it, I first have a conditional checking for it. So here, later in my code, I access that array after a conditional like this:
if (selectedProject
&& selectedProject.releases
&& selectedProject.releases.length) {
majorReleasesSet = new Set(selectedProject.releases.map((release: ReleaseType): string => (
release.major)));
projectHasNoReleases = false;
projectLatestMajorRelease = selectedProject.releases.slice(-1)[0].major;
projectLatestMinorRelease = selectedProject.releases.slice(-1)[0].minor;
}
But flow does not like this, complaining:
Cannot call selectedProject.releases.slice because property slice is missing in undefined [1].
components/TimeEntries/EntryForm.jsx
123│ release.major)));
124│ projectHasNoReleases = false;
125│ projectLatestMajorRelease = selectedProject.releases.slice(-1)[0].major;
126│ projectLatestMinorRelease = selectedProject.releases.slice(-1)[0].minor;
127│ }
128│
129│ const projectLatestRelease = `${projectLatestMajorRelease}.${projectLatestMinorRelease}`;
What in the world am I missing? The I tried adding Array.isArray(selectedProject.releases)
but flow still complained. Flow lists errors on both lines 125 and 126.
Upvotes: 1
Views: 277
Reputation: 3478
I would say that the the fact that you do some potentially effectful things (running a map function, making a set, etc) makes Flow concerned that the releases
property might have changed on the object. Flow will discard basically any of your refinements after you run a function.
Here's an example of your code, as-is, throwing an error
The easiest way to get around this is to pull the releases
off of your object into a separate value and perform the null check against that. That way, Flow is sure that it's still not null:
(Try)
// Mock this
type ReleaseType = {
major: string;
minor: string,
}
type ProjectType = {
// removed for brevity
releases?: Array<ReleaseType>
}
var selectedProject: ProjectType = {
major: "foo",
minor: "bar",
}
// Set up some variables for this example since I
// don't have any context on them
declare var majorReleasesSet: Set<any>;
declare var projectHasNoReleases: any;
declare var projectLatestMajorRelease: any;
declare var projectLatestMinorRelease: any;
// The `const {releases} = whateverObject` is the important part here
const {releases} = selectedProject || {};
if (releases && releases.length) {
majorReleasesSet = new Set(releases.map((release: ReleaseType): string => (release.major)));
projectHasNoReleases = false;
projectLatestMajorRelease = releases.slice(-1)[0].major;
projectLatestMinorRelease = releases.slice(-1)[0].minor;
}
Upvotes: 1