Skip to content

flow: Set up to use Flow enums; convert CreateWebPublicStreamPolicy#5444

Closed
chrisbobbe wants to merge 0 commit intozulip:mainfrom
chrisbobbe:pr-start-flow-enums
Closed

flow: Set up to use Flow enums; convert CreateWebPublicStreamPolicy#5444
chrisbobbe wants to merge 0 commit intozulip:mainfrom
chrisbobbe:pr-start-flow-enums

Conversation

@chrisbobbe
Copy link
Copy Markdown
Contributor

@chrisbobbe chrisbobbe commented Jul 20, 2022

Following the guide at https://flow.org/en/docs/enums/enabling-enums/ .

There were some surprises with getting ESLint quite on board with Flow enums, and there's at least one place where I haven't yet managed to fix something it does with switch statements, noted in the commit where I convert CreateWebPublicStreamPolicy. But I think I found a good workaround for that; see there.

Apart from making our own code nicer, this should help us be more confident using Flow enums in third-party type definitions. I've been working on some where the library's TypeScript uses TS enums: expo-application and expo-screen-orientation.

@chrisbobbe chrisbobbe requested a review from gnprice July 20, 2022 00:13
@chrisbobbe chrisbobbe force-pushed the pr-start-flow-enums branch from 38e2f98 to 9df5eb1 Compare July 20, 2022 00:20
@chrisbobbe chrisbobbe marked this pull request as ready for review July 20, 2022 00:21
@chrisbobbe chrisbobbe force-pushed the pr-start-flow-enums branch from 9df5eb1 to aa40a62 Compare July 20, 2022 00:28
@chrisbobbe
Copy link
Copy Markdown
Contributor Author

The runtime values seem to be working fine; I exercised one of the switch statements on a CreateWebPublicStreamPolicy value in manual testing. Specifically, the one in EditStreamCard; on CZO, I got the message "{realmName} only allows organization owners…", which was expected with CZO's settings.

@chrisbobbe chrisbobbe force-pushed the pr-start-flow-enums branch 5 times, most recently from 2813c0f to 0c8289b Compare July 20, 2022 03:38
@chrisbobbe
Copy link
Copy Markdown
Contributor Author

chrisbobbe commented Jul 20, 2022

Fixed CI. In

eslint: Use new hermes-eslint parser, instead of @babel/eslint-parser

, there was some (good) new ESLint feedback that didn't show up in my local testing. My local testing didn't include --all-files. I added details in the commit message.

@chrisbobbe chrisbobbe force-pushed the pr-start-flow-enums branch from 0c8289b to 58abd51 Compare July 20, 2022 03:54
Comment thread src/streams/EditStreamCard.js Outdated
Comment on lines +80 to +83
/* $FlowExpectedError[invalid-exhaustive-check]: TODO: Flow points
out that the default case can be removed because the other cases
are exhaustive. ESLint doesn't agree; fix that and remove the default
case. */
Copy link
Copy Markdown
Contributor Author

@chrisbobbe chrisbobbe Jul 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flow: Use a Flow enum for the first time! (CreateWebPublicStreamPolicy)

Pasted from the commit message, here's what ESLint reports when I remove the default case:

  /Users/chrisbobbe/dev/zulip-mobile/src/streams/EditStreamCard.js
    70:15  error  Expected to return a value at   consistent-return
                  the end of arrow function
    71:7   error  Expected a default case         default-case

Hmm. Thinking more about this, I think it's not realistic to expect consistent-return or default-case (or any ESLint rule) to recognize when the cases are exhaustive. In

eslint: Use new hermes-eslint parser, instead of @babel/eslint-parser

, we helped ESLint lint better by making it model Flow syntax more faithfully. But that doesn't mean ESLint will (or should) take that better model and go and do the type-checking to conclude that the cases are exhaustive. That would mean it's doing the job of a type-checker, and that's not its job.

So: can we repeal or adjust consistent-return and default-case?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the right answer here is to disable those two rules.

The one thing I see that we'll lose by doing so is that if you have a function whose return type includes void, then I'd like something like consistent-return to still apply: if there are any return statements with arguments, then I'd like to not have other control paths that implicitly return. That just makes it a bit easier when reading the code to be confident of spotting all the ways it returns.

But I don't think that's critical. The type-checker is taking care of everything we need here that's actually critical. We can cheerfully disable these rules.

Comment thread .eslintrc.yaml Outdated

#
# ================================================================
# Obsolete or redundant rules; repeal or adjust.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This heading reads to me like it's describing a task for us to do in the future, but after reading the contents I believe it's actually describing what this section does now.

I think I'd rather just keep these in the thematic organization we have below: the ft-flow/ lines would go under "Flow; plugin ft-flow.", etc.

Copy link
Copy Markdown
Contributor Author

@chrisbobbe chrisbobbe Jul 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree about the ft-flow/ lines and the react/ lines, since those are about rules from specific plugins, but I'm not yet sure about no-undef. Copying:

  # With hermes-eslint for `parser`, above, this rule can analyze Flow
  # types. It would fire on global types like $ReadOnly, for which it
  # doesn't see a declaration. We could find out how to teach the rule that
  # these types are globally defined…or just turn the rule off, because it's
  # redundant with Flow (specifically `Flow(cannot-resolve-name)`, I think):
  #   https://github.com/flow-typed/eslint-plugin-ft-flow/issues/33#issue-1274050123
  no-undef: off # https://eslint.org/docs/latest/rules/no-undef

no-undef is an out-of-the-box rule from ESLint, it's not from a plugin, so the right section is less obvious to me. It might go under "Airbnb rule repeals / adjustments"? But:

  • Is Airbnb responsible for bringing in this rule? I see airbnb under extends:, but I also see some other things there that could plausibly be asking for this rule. I guess the most efficient way to find out is to find each line's list of rules it brings in, and search those lists. I didn't find a way to make the ESLint CLI do this for me.
  • Is it important that Airbnb (etc.; see previous bullet) is bringing in this rule? Or do we want to reject it no matter where it came from, since it's redundant with Flow?

Copy link
Copy Markdown
Contributor Author

@chrisbobbe chrisbobbe Jul 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also see some other things there that could plausibly be asking for this rule

To expand on this, here's the extends::

extends:
  - airbnb
  - plugin:jest/recommended
  - plugin:react-hooks/recommended
  - ./tools/formatting.eslintrc.yaml

We can easily rule out ./tools/formatting.eslintrc.yaml; it's a path to a short file we maintain. And I already knew it doesn't mention no-undef.

But thinking conservatively about plugin:jest/recommended and plugin:react-hooks/recommended…I'd treat "recommended for use with Jest" and "recommended for use with React Hooks" as not really helping me know what's in the tin. I guess I should read code for eslint-plugin-jest and eslint-plugin-react-hooks in node_modules/, or find those on GitHub and turn to the specific versions we use, with help from yarn why

…ah, right, so I see that in each case, the plugin's "recommended" list only includes rules defined by the plugin itself, so in particular neither includes no-undef. That makes things neater, but I had to check, because nothing enforces that. (We know nothing enforces it because airbnb is in the same list, and it brings in lots of non-plugin rules that we have existing repeals for.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

…ah, right, so I see that in each case, the plugin's "recommended" list only includes rules defined by the plugin itself, so in particular neither includes no-undef. That makes things neater, but I had to check, because nothing enforces that.

Yeah. Nothing enforces that, but OTOH my sense is that that is a pretty firm convention among ESLint plugins: A ruleset like "plugin:foo/recommended", where "foo" is an ESLint plugin, consists of a subset of the rules that the plugin "foo" itself defines.

Effectively this convention substitutes for having a concept of "this rule is enabled by default" as part of a rule's definition.

Copy link
Copy Markdown
Member

@gnprice gnprice Aug 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Is it important that Airbnb (etc.; see previous bullet) is bringing in this rule?

Yeah, I think the "Airbnb rule repeals / adjustments" section heading may have outgrown its usefulness as part of the organization of this file.

... Hmm, in fact, I think that section never made much sense. It looks like it originates with 245ea9c followed by 1d870dd. Before that former commit, there wasn't anything like an "Airbnb repeals" section -- because that's what most of the rules block was about anyway.
https://github.com/zulip/zulip-mobile/blob/86a42582a/.eslintrc.yaml

So yeah, probably ideally the rules in this "Airbnb repeals" section would all just get moved into the appropriate thematic sections below.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The "Obsolete or redundant rules" section heading this comment thread was originally about is already gone in the current revision.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So yeah, probably ideally the rules in this "Airbnb repeals" section would all just get moved into the appropriate thematic sections below.

(That's out of scope for this PR, though.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That all makes sense; thanks!

Comment on lines +323 to +331
/* $FlowFixMe[incompatible-use] - CreateWebPublicStreamPolicy is a
Flow enum. Values are numbers, so they do have .toString…but
Flow reasonably hides that detail from consumers. For
CreateWebPublicStreamPolicy, it would actually be better to use
CreateWebPublicStreamPolicy.getName(initialStateValue), if we
find a nice way to write that with type checking. */
const initialStateLabel = initialStateValue?.toString() ?? '[nullish]';
// $FlowFixMe[incompatible-use] - As above.
const eventLabel = eventValue?.toString() ?? '[nullish]';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a good way to handle this would be to pull out a little helper function in this test file to contain this one line's worth of logic, with a name like labelFromValue. If nothing else, that would provide a single place for this comment.

Then that function could potentially grow some conditional logic to decide whether it should call getName instead of toString. Even if there were still some fixmes in the function, that'd be fine -- particularly as this is only in test code, and moreover just in computing the name for the test to use in the test-running UI.

Comment thread src/streams/EditStreamCard.js Outdated
Comment on lines +80 to +83
/* $FlowExpectedError[invalid-exhaustive-check]: TODO: Flow points
out that the default case can be removed because the other cases
are exhaustive. ESLint doesn't agree; fix that and remove the default
case. */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the right answer here is to disable those two rules.

The one thing I see that we'll lose by doing so is that if you have a function whose return type includes void, then I'd like something like consistent-return to still apply: if there are any return statements with arguments, then I'd like to not have other control paths that implicitly return. That just makes it a bit easier when reading the code to be confident of spotting all the ways it returns.

But I don't think that's critical. The type-checker is taking care of everything we need here that's actually critical. We can cheerfully disable these rules.

@gnprice
Copy link
Copy Markdown
Member

gnprice commented Jul 23, 2022

Thanks @chrisbobbe! I am also happy about how much cleaner this definition is, and the switch statements. Comments above.

@chrisbobbe chrisbobbe force-pushed the pr-start-flow-enums branch from 58abd51 to 0ca2c53 Compare July 26, 2022 01:34
@chrisbobbe
Copy link
Copy Markdown
Contributor Author

chrisbobbe commented Jul 26, 2022

Thanks for the review! Revision pushed, and I've sent a reply to one of your suggestions, above.

@chrisbobbe chrisbobbe force-pushed the pr-start-flow-enums branch from 0ca2c53 to d5569ab Compare July 26, 2022 16:30
@gnprice
Copy link
Copy Markdown
Member

gnprice commented Aug 3, 2022

Thanks for the revision!

Sorry for the delay on re-reviewing it, but I've looked just now at the first three commits, and merged those as:
d84ad12 eslint: Use better-maintained fork ("ft-flow") of "flowtyped" plugin
16d1182 flow-todo: Switch parser to @babel/eslint-parser, from babel-eslint
3b76083 eslint: Use new hermes-eslint parser, instead of @babel/eslint-parser

@chrisbobbe chrisbobbe force-pushed the pr-start-flow-enums branch from d5569ab to 055d9f3 Compare August 17, 2022 21:38
@chrisbobbe
Copy link
Copy Markdown
Contributor Author

Thanks for the review! I've just rebased atop main now that it includes those three commits; the six commits in this branch are ready for review.

Comment thread .eslintrc.yaml Outdated
# https://github.com/import-js/eslint-plugin-import/issues/2073
# Turn off; seems like this rule's purpose is covered by Flow; discussion:
# https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/Flow.20enums/near/1406529
import/named: off #https://github.com/import-js/eslint-plugin-import/blob/v2.26.0/docs/rules/named.md
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is this lack of space in #https://… intentional?

It reads a bit oddly; and it can make the link a bit more annoying to copy-paste into e.g. a browser.

Comment thread .eslintrc.yaml Outdated

#
# ================================================================
# Obsolete or redundant rules; repeal or adjust.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

…ah, right, so I see that in each case, the plugin's "recommended" list only includes rules defined by the plugin itself, so in particular neither includes no-undef. That makes things neater, but I had to check, because nothing enforces that.

Yeah. Nothing enforces that, but OTOH my sense is that that is a pretty firm convention among ESLint plugins: A ruleset like "plugin:foo/recommended", where "foo" is an ESLint plugin, consists of a subset of the rules that the plugin "foo" itself defines.

Effectively this convention substitutes for having a concept of "this rule is enabled by default" as part of a rule's definition.

@gnprice
Copy link
Copy Markdown
Member

gnprice commented Aug 31, 2022

Thanks for the revision!

All looks good except a small nit mentioned above. Please merge at will.

@chrisbobbe
Copy link
Copy Markdown
Contributor Author

All looks good except a small nit mentioned above. Please merge at will.

Merged, after fixing that. Thanks for the review!

@chrisbobbe chrisbobbe deleted the pr-start-flow-enums branch August 31, 2022 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants