-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Flow 0.53 support #1377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flow 0.53 support #1377
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems legit to me but i don't use flow
lib/rules/prop-types.js
Outdated
@@ -917,6 +917,12 @@ module.exports = { | |||
} | |||
}, | |||
|
|||
TypeParameterInstantiation: function(node) { | |||
if (node.params && node.params[0].type === 'GenericTypeAnnotation' && node.params[0].id.name === 'Props') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be best not to check for the type name here in case people want to pass in a type with a different name, eg:
type FancyPropsType = {
...
}
class Bar extends React.Component<FancyPropsType> { }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, it would be great to add that as a unit test to ensure it is supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call; this definitely should be supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense.
But what about this test case that we have. Is that still valid? I suppose not, as now we are passing void
for Props
and { person: Person }
as State
? What was the third parameter?
type Person = {
firstname: string
};
class Hello extends React.Component<void, { person: Person }, void> {
render () {
return <div>Hello {this.props.person.lastname}</div>;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the third might be context
Actually, the current code supports this syntax but Props has to be the second argument. Look at: https://github.com/yannickcr/eslint-plugin-react/blob/master/lib/rules/prop-types.js#L860 I suggest, we can make the argument location configurable? I suppose in the past, the second TypedParameter represented Props. Now the first one represent Props, and the second represents State. Or can we just hard-code it and only support the latest Flow version? All other changes from this PR should be able to be reverted by just switching WDYT? |
Hm, that's a pretty major breaking change - are we sure the order was flipped? |
According to https://medium.com/flow-type/even-better-support-for-react-in-flow-25b0a3485627 - Props is definitely the first argument. See quote below, it's a breaking change in Flow.
The other code got added in: #1236 - It does not look like there was a corresponding issue. There is a similar PR (that is still open, I think we can close it?): #1138 The doc links from the first PR (the one that got merged) now lead to docs that explain that Props is the first argument as well: https://flow.org/en/docs/react/components/ |
Yeah, I can confirm that it is indeed a breaking change for the |
I think we'll need a flow version setting just like we have for React. |
Great. I didn't know we have that. Makes sense, for rules like Replaced the original implementation with one that takes the Flow version in mind. 0.52 checks second argument. 0.53 checks first argument. Will add some more test cases for 0.53 (probably should duplicate all the test cases we already have?) and update docs. There's also still some shared code between "version" and "flowVersion". |
tests/lib/rules/prop-types.js
Outdated
@@ -1486,6 +1486,7 @@ ruleTester.run('prop-types', rule, { | |||
' }', | |||
'}' | |||
].join('\n'), | |||
settings: {react: {flowVersion: '0.52.0'}}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it better to have {flowVersion: '0.52.0'}
be the default so the eslint-plugin-react
upgrade is less likely to break things for people and not force them to have to add this?
Or, is it better to make 0.53.0
the default to encourage upgrades and easy adoption for new users?
Or, should we leave as is -- where there is no default and users have to make a conscious choice which they want to pick?
Any advice here @ljharb?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it depends on the next eslint-plugin-react
version? Major version bump means we can make the default to 0.53. Minor version bump means we need to stick to supporting 0.52 as the default (and allow users to configure to 0.53)?
BTW, the current default for React (and also in this PR for Flow) is 999.999.999
. So it just assumes by default "the latest version" if the setting is not specifically set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be coherent with the React version we should default to the latest supported version.
But currently a breaking change (or deprecation) in React means a new major version bump for the plugin. Are breaking changes a common thing in Flow ? (I'm afraid this will force us to bump the major version too often).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been using Flow since around version 0.40 (about 3-4 months) and this is the first breaking change I've encountered so far. I wouldn't say that breaking changes like this are very common.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we've checked this syntax in 0.52, it must be a breaking change to default to 0.53.
My strong preference is to initially release a semver-minor with the default as 0.52, and then later release a semver-major that bumps the default to Flow latest.
…he number of TypedArguments
Updated the PR according to the comment from James Kyle: #1376 (comment) if there are 3 TypedArguments then we can safely assume the second argument is the Props. If there are 1 or 2, then the first TypedArgument is the prop. This makes the code work for both old and new versions of Flow! I'm guessing the What do you think about this approach? |
I was not aware it was legal to omit one of the three types in prior Flow versions; given that, then I think we can safely up the default flowVersion (and we should still keep that setting for future changes) |
I think there is some confusion now... It was not legal to omit one of the three types in prior flow versions. So we can safely assume that with 3 arguments, the user is using older version of Flow and therefore the Props will be the second argument. For the new version, it is legal to omit one of the two types. Thus, we can safely assume if there is one or two arguments, that the user is using Flow 0.53 and therefore the Props is the first argument. At least, that's what I understood from the post. Please correct me if I am wrong. But not sure... maybe the |
I think we're on the same page; this won't be a breaking change. I think we should absolutely add flowVersion; it should not default to anything; and when flowVersion is absent only, it should use the fallback logic that you describe. In the future, if we want to add Flow-version-specific behavior, we'll already have a documented setting to use for it. |
Aha, yes, I agree that's a good idea! 🎉 We'll have to duplicate a ton of tests though as we'll need to test with both |
…sion, we detect the props argument position by the arguments length'
lib/rules/prop-types.js
Outdated
@@ -173,7 +174,7 @@ module.exports = { | |||
*/ | |||
function isSuperTypeParameterPropsDeclaration(node) { | |||
if (node && node.type === 'ClassDeclaration') { | |||
if (node.superTypeParameters && node.superTypeParameters.params.length >= 2) { | |||
if (node.superTypeParameters && node.superTypeParameters.params.length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use >= 0
here rather than relying on implicit truthiness checks
if (context.settings.react && context.settings.react.flowVersion) { | ||
confVer = context.settings.react.flowVersion; | ||
} else { | ||
throw 'Could not retrieve flowVersion from settings'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't this mean that if it's not specified, it throws?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. That's what we wanted, right?
We don't want to provide a default value, so test()
cannot return true
or false
in case it is not specified. Therefore, throwing an exception instead which we then handle in the calling code by catching it and using the default logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry if I wasn't clear; when no version is specified, I want the logic to be "guess which flowVersion it is" - specifying a flow version can't be required, or else it's a very large breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are guessing what the flowVersion is, but only for the specific implementation in the prop-types
rule: https://github.com/yannickcr/eslint-plugin-react/pull/1377/files#diff-531a13f7edd3ed4e7c65c3a7df7052f0R868
As this is a general utility function, I don't think that here we can easily guess what flow version we are using, because we need more information about the code. In the prop-types
example, we need to know how many TypedArguments
there are. But if there are none, then we cannot make a guess. If another rule would want to use this utility function, they would have to specify their own criteria/logic for guessing the correct flowVersion.
I can move this utility function into the prop-types
rule directly, as it is currently the only place where it is used. I just thought that for future cases it might be good to have it extracted already, so that it can be used in other rules as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, i see what you mean.
I guess that's OK, but i feel like maybe returning null
here would be easier to deal with than try/catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd still prefer if testFlowVersion returned null when absent instead of specifying (deferring to individual rules to throw if the version is ever required), but that's an internal implementation detail so we can tweak it later.
I can still change it. I prefer the try/catch slightly more because with a With the Additionally, with a let argumentPosition;
const isFlowVersion53 = testFlowVersion(context, "0.53.0");
if (isFlowVersion53 === null) {
// use default logic
} else {
argumentPosition = isFlowVersion53 ? 0 ; 1
} But in the grand scheme of things it's just a minor change and I can live with both implementations 😄 |
A totally different question:
The same for I think also that the "props extraction logic" should be extracted to a separate file (like we have |
Yes, it would be ideal if every rule supported this syntax; a separate PR is fine. If there's logic that can be shared, let's put it in a separate file in this PR, in anticipation. |
I'm sure there is logic that can be shared but I think it's too early to tackle it. We would have to figure out what exactly is already duplicated between these two rules and they are both quite big/complicated. I just stumbled upon this while looking at another bug that was just reported and noticed there is some duplication already. Extracting it will take time... |
Hi, can I check when this changeset will be released? Will it be the next minor or major release? |
@jseminck for some reasons, eslint fails to validate the props ( type Props = {|
className?: string,
width?: number,
minWidth: number,
range: number,
|};
export default class LoadingText extends React.Component<Props> {
} but this works: type Props = {|
className?: string,
width?: number,
minWidth: number,
range: number,
|};
export default class LoadingText extends React.Component<Props, void> {
} Any idea? |
@willdurand please file a new issue |
sure thing! |
A tip for people upgrading and it doesn't seem to work. If you're working in an IDE (e.g. IntelliJ), make sure you restart it after upgrading your dependencies. IntelliJ does something with caching so the new rules won't be used until after a restart. Everything is working fine afterward. Another hour lost to the ether... 🙄 Thanks @jseminck et. al for the hard work to make this happen! |
Attempted to add Flow 0.53 support: #1376
I'll have to admit that I am not very familiar with Flow, and am not fully sure if this covers all the use-cases... but this seemed like a rather urgent/important issue so perhaps it can help someone else to get to the correct solution.
But other than that, it does fix the example issue that was failing previously, so there is that 😄
Fixed #453.