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
Conditional Prop Incorrectly Required When Using Spreads/Desctructures #6863
Comments
This is the clearest presentation of a complex issue that I’ve seen in Flow is correct here. You note that But a valid implementation of function useRest(props: SpreadType) {
console.log(props);
props.foo = "hello";
} If you were allowed to call This explains why the shallow copy ( You can fix this by explicitly declaring that function useRest(props: $ReadOnly<SpreadType>) {
console.log(props);
} |
Thanks for the explanation. That makes sense, but leads to other questions: Why does Flow understand the shallow copy is safe to mutate (or safe from mutation?) but not the original Furthermore, if I shallow copy function test(all: AllProps) {
const { foo, baz, ...rest1 } = all;
rest1; // Flow types this as {| bar?: string |} (Expected)
useRest(rest1); // Error (Expected)
rest1.bar.toUpperCase(); // Error (Expected)
// An error in Flow? When expanding an object with optional 'bar' key, Flow makes the
// type required, and misses the type error.
const rest2 = { ...rest1 } // Flow types this as {| bar: string |} (Unexpected)
rest2.bar.toUpperCase(); // OK (Unexpected)
// This is unexpected. Flow seems to realize that it can broaden the type
// of rest3 to match the input type of `useRest`. This seems to be sound.
// But why does this only happen here, and not when rest is originally extracted
// on the first line of this function?
const rest3 = { ...rest1 } // Flow types this as {| bar?: string, foo?: string |}
useRest(rest3); // OK (...but why)
rest3.bar.toUpperCase(); // Error (Expected)
rest3.foo; // Typed as void | string
} |
It’s not that the shallow copy is “safe” to mutate; it’s that it’s at a As for why Flow understands it: it’s just part of the semantics for the (Let me know if this is unclear…)
I agree; this is inconsistent. The
You’re correct: this is definitely a bug in Flow. I reported it as #6549 |
Thanks for the detailed explanation! Much appreciated. |
Thanks for the explanations. It's clearer now. I think I was (and may still be) a bit confused by the inconsistencies in how rest was being typed. To clarify: there is an inconsistency in how Addendum: I'm using the $ReadOnly utility in the function expecting the spread type as a working solution. |
Now all examples work except for |
Description
We have a function used across several components which expects an exact object with all conditional props.
In a component where the function is used, we import this type and spread it into the component's props' type. This will allow overwriting the original props, because we don't want SpreadType to block any prop typing. (e.g.
foo
is now required and a boolean).To pass the component's props to the function, you must destructure and remove props not expected by the function, or props that you have overwritten.
Expected Result
We expect the
test
function to allow us to pass rest as an argument because the type is only{|bar?: string|}
.Actual Result
Even though rest is correctly typed as
{|bar?: string|}
, flow expects the overwritten propertyfoo
to be required in the object functions argument.Workaround
The only solution I found was to reassign the object by spreading the remaining properties into a new object.
I tried several other ways to address this, including explicitly typing the rest properties after assignment, but that didn't work. The is also no difference in typing the objects as exact or inexact.
Try Flow
Error
The text was updated successfully, but these errors were encountered: