typescript-vs-flowtype icon indicating copy to clipboard operation
typescript-vs-flowtype copied to clipboard

Write about Flow not accepting more strict Arrays

Open niieani opened this issue 8 years ago • 12 comments

function test(param : Array<number | string>) {
  return param
}

const compatibleVar : Array<number> = [1, 2, 3]

test(compatibleVar)

This works properly in TypeScript, but fails in Flow with the error:

3: function test(param : Array<number | string>) {
                                        ^ string. This type is incompatible with
7: const compatibleVar : Array<number> = [1, 2, 3]
                                ^ number

niieani avatar Feb 02 '17 09:02 niieani

Just to clarify: Flow is being more strict here, not TS

vkurchatkin avatar Feb 10 '17 12:02 vkurchatkin

@vkurchatkin I know it's being more strict, I just can't think of a reason why would you want this behavior. Can you give a reason as to why this is better?

niieani avatar Feb 10 '17 18:02 niieani

This is better because it's safe. Typescript's behavior is unsafe:

function test(param: Array<number | string>) {
  param.push('foo')
}

const compatibleVar : Array<number> = []

compatibleVar[0].toFixed(); // Runtime error

vkurchatkin avatar Feb 10 '17 18:02 vkurchatkin

@vkurchatkin ok, this makes sense (I'll add it to the README), but only if you're mutating data. It would be nice to be able to opt-in or opt-out of this, since with modern programming / immutability / functional dogma one really shouldn't be doing what test in the above example does (i.e. mutating the parameters).

I also think what you gave as an example is an edge case, so personally, I believe TypeScript's trade-off for a bit of unsoundness goes a long way in making it easier to develop in general.

(btw. you missed one line: test(compatibleVar), but I inferred what you were trying to say :))

niieani avatar Feb 10 '17 22:02 niieani

Well, in Flow you can just do:

function test(param: $ReadOnlyArray<number | string>) {
}

And it's going to work

vkurchatkin avatar Feb 10 '17 22:02 vkurchatkin

That's great to know, however this is an undocumented feature. :(

niieani avatar Feb 10 '17 23:02 niieani

Maybe this blog post can be helpful regarding this particular Flow behavior? https://medium.com/@forbeslindesay/covariance-and-contravariance-c3b43d805611#.6oemuj9gn

Unless you're already familiar with it, in any case maybe it helps for posterity.

leoasis avatar Feb 11 '17 15:02 leoasis

@leoasis I was aware of variance and how to use it (it's documented), it's only the $ReadOnlyArray that I didn't know about :) But thanks, the article is pretty useful!

niieani avatar Feb 11 '17 16:02 niieani

Probably good to talk about Flow's treatment of mutated arrays in the same section:

const arr = ['1', 0];
arr.sort();
// Error in TS, not in Flow, throws at runtime
arr[0].substr(0); 

RyanCavanaugh avatar Jun 06 '17 02:06 RyanCavanaugh

Interesting example, I've already filed this issue here: https://github.com/facebook/flow/issues/3735

The thing is, Typescript's behaviour seems to be not that different. In your example TS only catches the error because it doesn't try to infer precise type of arr. But if you add an annotation it's going to allow the same behaviour:

const arr: [string, number] = ['1', 0];
arr.sort();
arr[0].substr(0)

Flow, on the other hand, will show an error in this case.

vkurchatkin avatar Jun 06 '17 02:06 vkurchatkin

Flow, on the other hand, will show an error in this case.

Only because tuples are read-only. The equivalent TS code would be:

const arr: ReadonlyArray<number | string> = ['1', 0];
arr.sort();

or

const arr: { 0: string, 1: number } = ['1', 0];
arr.sort();

which both show the same error.

Probably TS tuples should inherit from some other Array type that allows mutation but doesn't allow reordering operations. Allowing sort / splice / etc on [number, string] is certainly wrong.

RyanCavanaugh avatar Jun 06 '17 02:06 RyanCavanaugh

Only because tuples are read-only. The equivalent TS code would be:

No, they are not really read-only, they just have methods of $ReadOnlyArray.

Probably TS tuples should inherit from some other Array type that allows mutation but doesn't allow reordering operations. Allowing sort / splice / etc on [number, string] is certainly wrong.

That's basically what Flow does

vkurchatkin avatar Jun 06 '17 02:06 vkurchatkin