rtfeldman / seamless-immutable

Immutable data structures for JavaScript which are backwards-compatible with normal JS Arrays and Objects.
BSD 3-Clause "New" or "Revised" License
5.37k stars 194 forks source link

File Objects Destroyed When Made Immutable #142

Closed dwighthouse closed 7 years ago

dwighthouse commented 8 years ago

File objects (and I suspect other special native objects such as FileLists and Blobs) are not correctly handled by seamless-immutable.

Here is a simple example. The user enters a File using the HTML5 file selection input. Once the File object is run through the Immutable function, the object's data (the filename, last modified time, size, type, and accessors necessary to interact with the data Blob) are all gone.

Try it yourself: https://jsfiddle.net/6jz5mxos/2/

This has to do with using Object.getOwnPropertyDescriptor in the initialization for...in loop in the Immutable function. Each property run through Object.getOwnPropertyDescriptor returns undefined, and so is not copied into the newly created immutable object.

Though it is possible to apply one's own properties to these types of objects (file.foo = 'bar';), like React components, I believe the vast number of use cases are to treat these types of objects as immutable, or at the very least, could be cloned at the time of the call to Immutable, since they tend to be overwritten whole, rather than partially modified.

What do you suggest? For now, I will be generating a key and building a second (mutable) store that simply knows that it contains references to these types of objects. Give it the key, it returns what it was given. The key will be some auto-created primitive value that can be safely stored in a seamless-immutable object.

ajhyndman commented 8 years ago

From my uninformed perspective, it looks like the real culprit here is that the Immutable.from factory is clobbering any pre-existing prototypes on objects we are freezing at any level of nesting.

I assume that the motivation is to remove any unexpected avenues through which mutation could sneak in. This seems sensible in most cases, but is destructive to some third-party APIs.

I notice that the seamless-immutable API accepts a custom prototype in it's options object for this purpose. I wonder if instead of accepting a single prototype to apply to everything, some of these issues might go away if the API accepts a function which can accept the pre-existing prototype and discard, preserve or augment it?

This seems like a reasonably accessible and non-invasive escape hatch to expose. Any thoughts?

https://github.com/rtfeldman/seamless-immutable/compare/master...ajhyndman:transform-prototype

rtfeldman commented 7 years ago

This seems worth special-casing in the same way we currently do with Dates.