Svelte: Destructuring a store object retuns the whole store if the property does not exist

Created on 26 Dec 2019  路  9Comments  路  Source: sveltejs/svelte

Describe the bug
When destructuring a store, the whole store object is returned if the property does not exist:

$: ({shouldBeUndefined} = $store);

It works if used with an existing property, i.e.:

$: ({existingProperty, shouldBeUndefined} = $store);

To Reproduce
You can find the REPL here

Information about your Svelte project:

  • Your browser and the version: Firefox 71
  • Your operating system: Windows 10
  • Svelte version : 3.16.7
  • Whether your project uses Webpack or Rollup : Rollup

Severity
Annoying

Thanks!

bug

All 9 comments

Interesting. The problem is that here where we're defining the $$invalidate function as (i, ret, value = ret) => { ... }, the third argument will default to being the same as the second argument - even if it was explicitly passed undefined, because that's how that works in javascript. I'm not sure what the most elegant way to fix this is. It would be nice to maintain the two-argument version of $$invalidate, because a lot of the time that's all we need. I guess we could look at arguments.length, but that doesn't feel great to me.

@Conduitry in which conditions value is passed to $$invalidate? I can't find any time were the compiler renders $$invalidate with a third argument: https://github.com/sveltejs/svelte/blob/a8b306f0a18775b31b57333d938090eb3934eb29/src/compiler/compile/render_dom/Renderer.ts#L151-L198

But anyway, maybe we could check if value is defined or not, do some stuff and then assign him ret's value?

The original REPL that reproduces this error is one where $$invalidate() gets called with three arguments - $$invalidate(1, { shouldBeUndefined } = $store, shouldBeUndefined);. Checking whether value is undefined won't help, because (like it is now where it defaults to ret) that doesn't distinguish between it not being passed at all and it being passed as undefined.

I see 3 ways to solve this:

1. Using arguments.length as @Conduitry said.

2. Creating two $$invalidate (for example: $$invalidateA, $$invalidateB). One with two arguments (i, ret) and one with three (i, ret, value).

3. Passing a single object though $$invalidate which contain three properties: i, ret, value. Then we can check if value is given in the object with something like:

!!Object.keys({ i: 1, ret: { value: 'test' }, value: undefined }).find(e => e === 'value') // true

!!Object.keys({ i: 1, ret: { value: 'test' } }).find(e => e === 'value') // false

I haven't tried this yet, but what seems like it might be a good solution is to make value be the second argument of the $$invalidate() function, and to have ret be the third and default it to value.

I guess you mean (i, value, ret = value) => { ... }, then I also guess it should be changed in the compiler?
But even, how could this solve the issue?
I'm sorry if I ask too much questions, I'm just trying to understand the project so I can try to contribute..

@AnatoleLucet The way the method is currently set up, if value is undefined then it is made to equal ret, which means if value was explicitly undefined, there is no way of knowing (since it is then set to ret). If changed around as @Conduitry proposed, value could be explicitly undefined. I also don't think it would require any rewriting for the compiler, since it would work roughly the same as it did previously, with the exception of being able to handle explicitly undefined values.

Thanks !

And by the way, congratulation for this great framework :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

plumpNation picture plumpNation  路  3Comments

davidcallanan picture davidcallanan  路  3Comments

1u0n picture 1u0n  路  3Comments

matt3224 picture matt3224  路  3Comments

AntoninBeaufort picture AntoninBeaufort  路  3Comments