Skip to content
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

Empty object not of exact type with optional properties #2386

Closed
minedeljkovic opened this issue Sep 3, 2016 · 18 comments
Closed

Empty object not of exact type with optional properties #2386

minedeljkovic opened this issue Sep 3, 2016 · 18 comments
Labels

Comments

@minedeljkovic
Copy link

minedeljkovic commented Sep 3, 2016

Minimal example:

type PossiblyEmpty = $Exact<{
  a?: number
}>

const p: PossiblyEmpty = {} // this errors. Should it?

https://flowtype.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgAKcAziQJYBGMWAogLY7ZgC8YAJLQB4CGAxhgA8Ab1RgwPAPwAuMADsArvUp4ATqgC+APnR84ckhjA5ZxMlRoMmWVmGEbUQA

Is this by design?

EDIT: inline an example

@samwgoldman samwgoldman added the bug label Sep 3, 2016
@samwgoldman
Copy link
Member

Yeah, looks like a bug to me. Thanks for the minimal example!

@samwgoldman
Copy link
Member

For what it's worth, I am responsible for making this an error (in this commit). In general, unsealed things can't be exact, because other references to them can widen the type in unexpected ways.

In this case, there are no other references, so it doesn't really make sense to consider the object literal to be unsealed. I think the fix here is to not create an unsealed object in this case. var o = {} should be unsealed, but var o: T = {} should not be.

@danez
Copy link
Contributor

danez commented Nov 18, 2017

Still getting the error. Is that something that will be fixed?

@Will-Wrightwa
Copy link

I would also like to see this fixed. Anytime you are using an options parameter it is helpful to allow {}

@vezaynk
Copy link

vezaynk commented Nov 28, 2017

@samwgoldman It's still an issue!

@chrisblossom
Copy link

Look at #4582 (comment) for a workaround

@chrisblossom
Copy link

@lgraziani2712 Why the thumbs down? Does it not work correctly? Please share your experience.

@lgraziani2712
Copy link

lgraziani2712 commented Dec 14, 2017

The OP presents an object type with an optional attribute, while that workaround doesn't take in consideration those props. I tested myself with my object full of optionals attributes and it didn't work.

@chrisblossom
Copy link

@lgraziani2712 does this not provide your expected results? https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgCiYAvGAN5iphgCGA-AFxgDOGATgJYB2A5gDTUqNYMDAB1OOwDWtdnACu3ACZgoUsHgAetAMYYw2fADoJU6SzqXt+fXmVDRYBAAs83MMry0YPXs84MFzA4ACMAKzx9SyDaA11aD1CCPABbHGxjIQBtRKwAXWY0jKwqAF90XThuNk1mYjJyMrAnbjhNdnl2Fkrq2qh60gowUMYARjBmpzxOqUtaay1bDHsgA

@lgraziani2712
Copy link

lgraziani2712 commented Dec 14, 2017

Ok yep, it worked. I did it wrong: I kept the {||} exact pipes (Edit: when I tested before).

Thank you @chrisblossom

lgraziani2712 added a commit to lgraziani2712/codimo that referenced this issue Jan 19, 2018
@julienw
Copy link
Contributor

julienw commented Jul 3, 2018

Here is another example.

type Breakdown = {| [string]: number |};
const breakdown: Breakdown = {};

=>

    2: const breakdown: Breakdown = {};
                                    ^ Cannot assign object literal to `breakdown` because inexact object literal [1] is incompatible with exact `Breakdown` [2].
        References:
        2: const breakdown: Breakdown = {};
                                        ^ [1]
        2: const breakdown: Breakdown = {};
                            ^ [2]

I think the explanation given in #2386 (comment) is the right one. But it's been a while...

@ogard
Copy link

ogard commented Jul 25, 2018

Here is a possible workaround until bug is not fixed:

/* @flow */

declare type Criteria = {| option1?: number, option2?: string |};

declare export function init(criteria: Criteria): void;

const test = init({ ...undefined });

@NilSet
Copy link
Contributor

NilSet commented Oct 24, 2018

Another workaround is $Shape:

/* @flow */

declare type Criteria = $Shape<{option1: number, option2: string}>;

declare export function init(criteria: Criteria): void;

init({}); // ok
init({option1: 3}) // ok
init({option1: 3, rogueProp: 'secret'}) // errors as expected

@benjaminjkraft
Copy link

Another workaround, which to my mind is the cleanest of any I've seen here, is to just pass an explicit undefined. For many purposes the following is equivalent to an empty object:

({foo: undefined}: {|foo?: number, bar?: number|});

@penx
Copy link
Contributor

penx commented Apr 15, 2020

Recommend you add the label 'unsealed objects' to this and probably close #2977 as a duplicate of this so that progress/conversation can be tracked in one place.

@Ashoat
Copy link

Ashoat commented Jun 24, 2020

#7624 is also a duplicate

@gnprice
Copy link
Contributor

gnprice commented Sep 18, 2020

Another workaround for this issue is to use Object.freeze (playground):

({ x: 3 }: {| x: number |}); // ok

({}: {||}); // error

(Object.freeze({}): {||}); // ok (workaround)

I learned this from the thread at #2977; repeating it here in case it helps others, because this seems to be the canonical thread for this issue.

If you weren't planning to mutate the object, then this workaround is quite clean because it should have no effect at all on the behavior of the code that consumes the object. And in situations where you are planning to mutate the object, I think you typically want the "unsealed object" behavior anyway so this isn't an issue in the first place.

@gkz
Copy link
Member

gkz commented Dec 19, 2022

@gkz gkz closed this as completed Dec 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests