If an item is either a number or a string, I can assign a string to it. Similarly I'd expect that if a variable is an array of (numbers or strings) then I should be able to assign an array of strings to it -- but no...
// @flow
var foo: Array<string> = ["test"];
var bar: Array<string|number> = foo; // Flow error
var f: string = "test";
var g: number|string = f; // ok
Element-wise subtyping is unsound because Arrays are mutable. Consider the following:
var foo: Array<string> = ["test"];
var bar: Array<string|number> = foo; // Imagine this worked...
bar.push(0); // `foo` is no longer `Array<string>`
Interesting (and valid) point! Now of course in my code i'm doing a clone of foo, but there seems to be no way Flow can figure that out. So I guess I need to do a typecast in such a situation:
var bar: Array<string|number> = (foo: Array<any>);
Yeah we need immutable data structures for this. Hope TC39 will give us something to work with.
I've run into a similar issue before with Kefir streams (think RxJS if you're familiar with that). Kefir streams are immutable; given a stream, you can only listen on it for values it emits. I have a type declarations file so that streams can be typed. I can have a variable of type Kefir.Stream<string>, but I'm unable to pass it to a function that expects Kefir.Stream<string|number>. Currently I unsafely cast it to Kefir.Stream<any> and then to the final type when I need to do that.
I can also imagine running into similar issues with the Immutable.js library, despite these cases actually being safe. Could Flow add some feature to allow element-wise subtyping to be opted into for specific types so that declarations for Kefir and Immutable.js could use them? (Maybe a type can be marked as being an immutable object/handle? And then that feature could be used later on for native immutable data structures.)
@AgentME Yeah, Promise and Generator have special variance logic for this, but there's no syntax currently to specify variance in user-defined types. In the case of Kefir.Stream<string> and Kefir.Stream<string|number>, element-wise subtyping isn't involved. I doubt very much that we will allow unsound element-wise subtyping of mutable objects. What is more likely is that we will track mutation in order to allow element-wise subtyping when it is safe to do so. Furthermore, we could be more savvy about frozen objects, where element-wise subtyping is also safe.
What is more likely is that we will track mutation in order to allow element-wise subtyping when it is safe to do so. Furthermore, we could be more savvy about frozen objects, where element-wise subtyping is also safe.
Would that work in declaration files? Could someone write declarations for Kefir streams or Immutable.js that give them the same type variance features as Promise and Generator?
For variance, a hypothetical interface Foo<+T> or interface Foo<-T> to signify co- and contra-variance, respectively, is what I'm imagining.
Closing, the original question have been answered
Most helpful comment
Element-wise subtyping is unsound because Arrays are mutable. Consider the following: