-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Exact type doesn't work with possible empty object #2977
Comments
There is the simplest example: ({}:{||}) |
Related #2386 |
Exact types are really a key feature in flow, but so many puzzling issues with it. @mroch @samwgoldman Should we avoid using exact types? Or are there any workaround to this issue? Would love to help... |
@skovhus try #4582 (comment) for a workaround. |
@chrisblossom thanks... I might be missing something, but the example linked to simply gets rid of the exact type. |
Thanks @chrisblossom! |
I've encountered the same issue and solved with type E = $Shape<{
a: string,
}> Is this correct use? |
|
Sometimes `{ [any]: empty }` is used as a workaround instead of `{||}`. See: - facebook/flow#2977 - facebook/flow#4582
Sometimes `{ [any]: empty }` is used as a workaround instead of `{||}`. See: - facebook/flow#2977 - facebook/flow#4582
@skovhus I don't understand what you're saying the problem with |
@jedwards1211 I was wrong. Thanks @shinout! I like this solution better than the |
@skovhus okay cool, I was wondering if there were issues on past versions of flow or something. |
Please also note #5884 when using the |
There is no official solution for this yet, right? |
Flow 0.70 has new warnings with spreading non exact object which makes this more important to fix i think |
This works for most use-cases I have: https://stackoverflow.com/a/44444793/3098651 (Object.freeze({}): {||}) |
type A = {| a?: string |};
const x: A = (Object.freeze({})) I don't think this is a bug; it sounds like expected behaviour because https://flow.org/en/docs/types/objects/#toc-unsealed-objects |
Has this been changed with Flow 0.72? |
@mrkev I do not think the Honestly this issue has been a major issue for me, and probably others. The workarounds all have caveats and do not always work as expected. |
@chrisblossom I think the semantics are a little confusing to catch at first, but it's due to to sealing. I do recommend you give the docs on the matter another skim (: The issue is if an object is unsealed, it can't be exact because you can add any key/value to it. This allows you to use objects as maps in a very javascript-y way. What this issue essentially reduces to is that there's no good way of creating a sealed empty object, other than initializing it with some property and then deleting the property, or freezing it. I didn't close the issue because this can be considered a feature request, but I'd be hesitant to call it a bug because everything is working as expected. EDIT: I do completely agree though, the whole notion of Also, for the sake of reference, this is what I mean by initializing with some property and deleting it: type A = {| a?: string |};
const x: A = { a: "foo" } // sealed object
delete x.a; // x should now be empty EDIT 2: I'll add the feature request flag |
@mrkev this definitely is not "feature request" it's a bug and flow can have Exact object types where type is exact but object is not frozen at runtime on the other hand type ObjectWithName = {| +name: string |}
const data1: ObjectWithName = { name:'cotne' }
const data2: ObjectWithName = Object.freeze({ name:'cotne' })
type ObjectWithoutName = {| |}
const data3: ObjectWithoutName = {}// this should work as well
const data4: ObjectWithoutName = Object.freeze({}) name prop is removed from value and type but somehow it broke flow |
@TrySound how is this related to "unsealed objects" ? because it's definitely reports errors with Exact objects. how else you would declare exact empty object without freezing it at runtime? |
@thecotne Empty object is always unsealed. This is the source of the problem here. Isn't it? |
@TrySound yes (probably) flow treats empty objects specially so that you can cast any object to exact object type except empty ones maybe i don't understand "unsealed objects" label i was thinking that "unsealed objects" label is for bugs that accrue in unsealed objects (that are actually unsealed and not treat as such) |
Empty object is unsealed for the purpose. It's not a bug. Though the purpose makes it unsound in some cases. So I label it as a problem with unsealed objects. |
what is purpose of that? and is there any option that can disable it? (maybe in strict mode) |
I guess for this one. It's often used in facebook AFAIK. I think guys are working on new object model now and will provide a solid solution. |
and after that it's not type checked at all right? #2327 i think this should be disabled in strict mode (or in any mode) it is always possible to mark something as like this const a: any = {
// a: 1
}
if (2 < 3) {
a.b = 2;
} else {
a.c = 3
} works even if object is not empty in the first place const a: any = {
a: 1
}
if (2 < 3) {
a.b = 2;
} else {
a.c = 3
} |
It's still type checked. That's the point. Empty object has more similar use cases as unsealed. We may bikeshed about it forever. Let's see what guys will propose. |
Isn't this a duplicate of #2386? |
The only correct workaround for this 4+ years old bug is: |
@FlavienBusseuil Try |
@TrySound, I'm not a huge fan of injecting weird syntax to fix identified bugs. I would much prefer an official fix to remove my |
@FlavienBusseuil The thing to be aware of there is that using a FlowFixMe to ignore this is far more dangerous in that it sacrifices type safety entirely, both because it allows for introducing unsealed objects (whose mere existence is a flow bug as far as I'm concerned) and because it just switches off the type system for that line. For my team, I actually ended up writing a custom auto-fixing eslint rule that we use with const unsealedObjectError = () => 'Flow interprets empty objects literals as unsealed objects,' +
' see https://git.io/fj64j';
module.exports = {
'no-unsealed-objects': {
meta: {
docs: {
description: 'no unsealed objects',
category: 'Possible Errors',
recommended: false,
},
schema: [],
fixable: 'code',
},
create: (context) => {
// ignore fixture data
const filename = context.getFilename();
if (filename.match(/fixtures/)) return { ...null };
return {
ObjectExpression: (node) => {
if (node.properties.length === 0) {
context.report({
node,
message: unsealedObjectError(),
fix: fixer => fixer.replaceText(node, '{ ...null }'),
});
}
},
};
},
},
}; |
But then to avoid the creeping scourge of wasteful operations to appease flow, you should use a babel plugin to turn |
The other problem with |
@jedwards1211 type A = {|foo?: number|}
const a: A = {/*:: ...null */} Although this is of course even weirder looking piece of code for the next reader... |
by using a comment, at least no devs who are less familiar with JS would think there's some obscure runtime purpose to doing |
This
combined with this
makes me 😭 Another nasty solution is to do |
Fixed if you enable the There will be a blog post announcement in the future |
Repro
Flow version 0.36
The text was updated successfully, but these errors were encountered: