Comments (23)
Immutability is now supported!
Please let us know if this does not cover all of your use cases.
If you want to assume that all function arguments are immutable we have an experimental option experimental.const_params
. We want to explore making params constant by default and then disable that assumption if you assign to the function argument.
const a: {
+prop: number
} = {
prop: 42,
};
const b: $ReadOnlyArray<number> = [1, 2, 3];
const c = Object.freeze({
prop: 42,
});
a.prop = 1; // Error
b[0] = 2; // Error
c.prop = 3; // Error
from flow.
This feature basically exists now.
When using an object, you can mark all keys as covariant which makes them read-only. Plus, use an exact type to avoid allowing extra keys as well.
type ImmutablePerson = {|
+name: string,
+age: number
|};
As for Arrays, you can use $ReadOnlyArray
instead of Array
.
There are still edge-cases when dealing with other in-built Objects such as Date, Function etc. but that seems like enough of an edge-case.
I think we should close this issue now.
from flow.
Idea: a contract of immutability for objects and arrays.
E.g.
function foo <T> (x : Immutable<T>) : number {
x.value = 1; // ERROR: `x` is Immutable<T>
return x.value;
}
the contract is also considered broken if you call any function that doesn't implement the contract:
function bar <T> (x : T) : number {
x.value = 1;
return x.value;
}
function foo <T> (x : Immutable<T>) : number {
return bar(x); // ERROR: `x` is Immutable<T>, bar expects mutable T
}
function qoo <T> (x : Immutable<T>) : number {
return x.value;
}
function doo <T> (x : Immutable<T>) : number {
return qoo(x); // this is OK
}
function too <T> (x : T) : number {
return x.value;
}
function hoo <T> (x : Immutable<T>) : number {
return too(x); // this is OK too, b/c can be statically analysed
}
from flow.
+1, but -1 to overloading the keyword const
.
from flow.
I know that saying '+1' is extremely annoying, but... +1 here :) I would love this feature.
I wanted to somehow use Immutable Record for this, but it seems it's not very flexible no matter how I try to hack it to work with Flow (no way to capture disjoint unions, for example).
from flow.
Facebook's immutable.js offers immutable objects:
var foo1 = Immutable.Map({bar: 'baz'});
var foo2 = foo1.set('bar', 'biz');
foo1.get('bar'); // 'baz'
foo2.get('bar'); // 'biz'
Presumably most of immutable.js's functions could be annotated as pure.
from flow.
I'd say that if starting of scratch the bias here would be to assume all arguments are read-only, since this is the common and safe case, and require annotations when an argument can be mutated .
function append(a: string[], b: string) {
return a.push(b)
}
==> ERROR: Call to push mutates a read-only argument
function append(a: var string[], b: string) {
return a.push(b)
}
==> Flow checks sucessfully.
In any case having a way to express this would be a clear win for type-safety.
from flow.
I just ran into a related mistake that ideally flow would be able to catch:
function thingy(x) {
var foo = Immutable.Map({bar: 'baz'});
if (x) foo.merge({foo: 1});
return foo;
}
The mistake here is that the line should read return foo.merge
, because the .merge
function is entirely pure it must be an error to not use the return value in any way.
from flow.
What about creating something similar to inout parameter in swift
- Function parameters are immutable by default.
- Trying to change the value of a function parameter from within the body of that function results in a flow error.
- If you want a function to modify a parameter’s value, define that parameter as an in-out
from flow.
In terms of naming, would like to throw another option into the hat $Frozen<type>
eg
$Frozen<{ value: ?string }>
$Frozen<Array<number>>
from flow.
I was thinking about having a type which is normally mutable, but defining a function which uses that type in a read-only way. I don't think that can be done with per-property covariance or a lint rule?
from flow.
This is a much requested feature. The main issue is syntax: we need to be careful to design this while keeping future versions of JS in mind. No doubt it would be very useful.
from flow.
The other issue that I see here is that there is no existing standard way of "altering" an immutable object.
var immutable foo = {bar: 'baz'};
foo.bar = 'biz'; // should return a new foo object with a changed property.
The naive way of doing this would be to create a new object with the needed property and add the existing immutable object to its prototype:
function immutableSet(obj, prop, val) {
var props = {};
props[prop] = {value: val};
return Object.create(obj, props);
}
immutableSet(foo, 'bar', 'biz');
It would be nice if flow was able to de-sugar to something like this.
from flow.
I think the important thing here is the const
annotation. Persistent data structures are a whole different topic.
from flow.
Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed.
from flow.
I like the proposal from @jussi-kalliokoski to solve the syntax issue. Just add a magic type called Immutable
or NoMutate
or something else. (Immutable is probably not a good idea since Immutable.js exists)
But further, it's very important to add a way to annotate object and class methods as non-mutating.
So that this is no longer a type error:
var map: Immutable.Map<string, number>
var x = map.get('x') && map.get('x') > 10
As of right now, Flow thinks that .get
could mutate the object map
itself, and so the &&
doesn't work.
Instead, the workaround for now is:
var map: Immutable.Map<string, number>
var xVal = map.get('x')
var x = xVal && xVal > 10
However, it's not the end of the world to have to define a few extra variables for type-safety. So I support the team focussing on the general use case before tackling these features.
from flow.
An other use case for Immutabilty would also be to solve type casting errors
like in this example:
/* @flow */
type Foo = { foo: boolean }
type Bar = Foo & { bar: boolean }
const bars: { [key: string]: Bar } = Object.freeze({})
const foos: { [key: string]: Foo } = bars // generate an error
/* @flow */
type Foo = { foo: boolean }
type Bar = Foo & { bar: boolean }
const bars: { [key: string]: Bar } = {}
const foos: { [key: string]: Foo } = { ... bars } // work around
/* @flow */
type Foo = { foo: boolean }
type Bar = Foo & { bar: boolean }
const bars: Array<Bar> = []
const foos: Array<Foo> = bars.slice() // generate an error
/* @flow */
type Foo = { foo: boolean }
type Bar = Foo & { bar: boolean }
const bars: Array<Bar> = []
const foos: Array<Foo> = [...bars] // workaround
from flow.
The caveat with a special Immutable
type (or any way to signify immutability) would be that it only works shallowly.
type ImmutableRecordSomething = $Immutable<{ value: ?string, child: { value: ?string } }>;
const record: ImmutableRecordSomething = {
value: 'a',
child: { value: 'b' },
};
record.value = null // Flow error, record is immutable
record.child.value = null // Just fine, only record is protected normally
although that could be solved by doing child: Immutable<{ value: ?string }>
I guess.
Another issue would be passing it to other functions. If they are not set up to use flow, they would invalidate the immutability.
type ImmutableRecord = $Immutable<{ value: ?string }>;
const record: ImmutableRecord = { value: 'a' };
record.value = null // Flow error, record is immutable
// Assuming that lodash' mapValues has no flow types and it can't infer immutability
const newRecord: ImmutableRecord = _.mapValues(record, (x) => x);
record.value = null // No error, record is downgraded to mutable because of mapValues
from flow.
@cloudkite that could be confusing, or maybe colliding with a possible type for objects that have actually been Object.freeze()
-ed
@pgherveou As much as I would love to have explicit mutability like this, I think flow should typecheck on working javascript code, and in javascript you are allowed to mutate your parameters :-/
from flow.
I think $Frozen<T>
makes sense as it would just be a way to describe the result of Object.freeze(T)
, which is already supported by Flow.
Then the definition of Object.freeze
could be updated like so:
declare class Object {
static freeze<T>(o: T): $Frozen<T>;
}
Might try to make a PR for this just to get discussion going and also as a good excuse to learn OCaml 😄
from flow.
I think it would still be useful to mark an argument as read-only, as opposed to properties.
from flow.
@glenjamin That can easily be done with a lint rule.
from flow.
Thanks! This sounds great. Does it enforce the immutability of nested objects and arrays? And is it possible to annotate a function declaration to guarantee that a return value will be constant?
from flow.
Related Issues (20)
- flow_parser.js file increased from 0.7M to 2.7M in v0.210 HOT 4
- `Omit` does not work well with unions HOT 3
- ☂️ Known unsoundness issues
- Partial type does not accept plain empty object literal as a default HOT 5
- Cannot return class type as T: Cannot return `root(...)` because a call signature declaring the expected parameter / return type is missing in `SomeModel` [1] but exists in function type [2].Flow(prop-missing) HOT 2
- Does not narrow down type after undefined/falsy check HOT 1
- Recursive/deep Partial HOT 2
- Bugs in negated `instanceof` expressions HOT 1
- [Question]: How to fix flowgen test after flow update HOT 3
- [Discussion]: Any plans to make Flow more interpopble with TS ecosystem? HOT 5
- Higher order type parameter support HOT 1
- Readme links to 404
- Strange index signature error if you use $Keys<O> instead of the equivalent literal union inside $ReadOnlyArray HOT 1
- [homebrew] automation script needs update HOT 2
- "Unxepcted identifier, expected the token ;" when using await inside of ES module HOT 3
- Can't iterate through chars in a string HOT 4
- Flow does not catch invalid switch statements HOT 1
- Conditional type only works if I inline it HOT 5
- Object with null prototype does not extend interface {} HOT 3
- Missing documentation for "render-types"? HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flow.