newtests/array_literal_tuple_spread/test.js (349 lines of code) (raw):
/* @flow */
import type {Suite} from "flow-dev-tools/src/test/Suite";
const {suite, test} = require('flow-dev-tools/src/test/Tester');
module.exports = (suite(({addFile, addFiles, addCode}) => [
test("any flowing to spreads", [
addCode(`
function withoutAny(tup: [1,2], notAny: [3, 4]): [1, 2, 3, 4] {
return [...tup, ...notAny];
}
function withAny(tup: [1,2], any: any): [1, 2, 3, 4] {
return [...tup, ...any];
}
`)
.noNewErrors()
.because('Adding any should not cause new errors'),
]),
/* We used to try to "summarize" elements of non-tuple arrays, which would
* strip away literal information from string and number types. However, this
* was pretty broken, and Sam found this following example to demonstrate how.
*/
test("Sam's example of multiple lower bounds and SummarizeT", [
addCode(`
function f(b: boolean): [Array<?number>] {
var x = null;
if (b) {
x = 0;
}
var [xs] = f(b);
return [[...xs, x]];
}
`).noNewErrors()
.because(
'x has multiple lower bounds, so if we unify prematurely we can get ' +
'an error when figuring out the summarized element type for x',
),
]),
test('Avoid infinite recursion due to a loop', [
addCode(`
let foo = [0];
for (let x = 1; x < 3; x++) {
foo = [...foo, x];
}
(foo: [0, 1, 2]);
`)
.newErrors(
`
test.js:8
8: (foo: [0, 1, 2]);
^^^ Cannot cast \`foo\` to tuple type because array literal [1] has an arity of 1 but tuple type [2] has an arity of 3. [invalid-tuple-arity]
References:
4: let foo = [0];
^^^ [1]
8: (foo: [0, 1, 2]);
^^^^^^^^^ [2]
test.js:8
8: (foo: [0, 1, 2]);
^^^ Cannot cast \`foo\` to tuple type because array literal [1] has an arity of 2 but tuple type [2] has an arity of 3. [invalid-tuple-arity]
References:
6: foo = [...foo, x];
^^^^^^^^^^^ [1]
8: (foo: [0, 1, 2]);
^^^^^^^^^ [2]
test.js:8
8: (foo: [0, 1, 2]);
^^^ Cannot cast \`foo\` to tuple type because array literal [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]
References:
6: foo = [...foo, x];
^^^^^^^^^^^ [1]
8: (foo: [0, 1, 2]);
^^^^^^^^^ [2]
test.js:8
8: (foo: [0, 1, 2]);
^^^ Cannot cast \`foo\` to tuple type because number [1] is incompatible with number literal \`1\` [2] in index 1. [incompatible-cast]
References:
5: for (let x = 1; x < 3; x++) {
^^^ [1]
8: (foo: [0, 1, 2]);
^ [2]
test.js:8
8: (foo: [0, 1, 2]);
^^^ Cannot cast \`foo\` to tuple type because number [1] is incompatible with number literal \`2\` [2] in index 2. [incompatible-cast]
References:
5: for (let x = 1; x < 3; x++) {
^ [1]
8: (foo: [0, 1, 2]);
^ [2]
test.js:8
8: (foo: [0, 1, 2]);
^^^ Cannot cast \`foo\` to tuple type because number [1] is incompatible with number literal \`2\` [2] in index 2. [incompatible-cast]
References:
5: for (let x = 1; x < 3; x++) {
^^^ [1]
8: (foo: [0, 1, 2]);
^ [2]
`,
),
]),
test('Avoid infinite recursion due to polymorphic recursion', [
addCode(`
function foo<T: Array<any>>(arr: T) {
if (arr.length > 10) return arr;
return foo([...arr, 1]);
}
const ret = foo([1]);
`),
addCode('(ret: void);')
.newErrors(
`
test.js:11
11: (ret: void);
^^^ Cannot cast \`ret\` to undefined because array type [1] is incompatible with undefined [2]. [incompatible-cast]
References:
4: function foo<T: Array<any>>(arr: T) {
^ [1]
11: (ret: void);
^^^^ [2]
`,
)
.because('The constant folding should turn the tuple into an array'),
addCode(`
(ret[5]: 1);
(ret[5]: 2);
`)
]),
test('Avoid infinite recursion due to recursion', [
addCode(`
function foo(arr) {
if (arr.length > 10) return arr;
return foo([...arr, 1]);
}
const ret = foo([1]);
`),
addCode('(ret: void);')
.newErrors(
`
test.js:11
11: (ret: void);
^^^ Cannot cast \`ret\` to undefined because array literal [1] is incompatible with undefined [2]. [incompatible-cast]
References:
6: return foo([...arr, 1]);
^^^^^^^^^^^ [1]
11: (ret: void);
^^^^ [2]
test.js:11
11: (ret: void);
^^^ Cannot cast \`ret\` to undefined because array literal [1] is incompatible with undefined [2]. [incompatible-cast]
References:
8: const ret = foo([1]);
^^^ [1]
11: (ret: void);
^^^^ [2]
`,
)
.because('The constant folding should turn the tuple into an array'),
addCode(`
(ret[5]: 1);
(ret[5]: 2);
`)
.newErrors(
`
test.js:15
15: (ret[5]: 2);
^^^^^^ Cannot cast \`ret[5]\` to number literal \`2\` because number [1] is incompatible with number literal \`2\` [2]. [incompatible-cast]
References:
8: const ret = foo([1]);
^ [1]
15: (ret[5]: 2);
^ [2]
`,
)
.because('The element type should be `1`'),
]),
test('Spreading in a tuple should produce another tuple', [
addCode(`
var a = [2];
var b = [4, 5];
var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
`).newErrors(
`
test.js:6
6: var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
^^^^^^^^^^^^^^^^^^^^^ Cannot assign array literal to \`x\` because number [1] is incompatible with number literal \`20\` [2] in index 1. [incompatible-type]
References:
4: var a = [2];
^ [1]
6: var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
^^ [2]
test.js:6
6: var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
^ Cannot assign array literal to \`x\` because number [1] is incompatible with number literal \`30\` [2] in index 2. [incompatible-type]
References:
6: var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
^ [1]
6: var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
^^ [2]
test.js:6
6: var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
^ Cannot assign array literal to \`x\` because number [1] is incompatible with number literal \`60\` [2] in index 5. [incompatible-type]
References:
6: var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
^ [1]
6: var x: [1,20,30,4,5,60] = [1, ...a, 3, ...b, 6];
^^ [2]
`,
)
]),
test('Explicit union should become an explicit union', [
addCode(`
function test(arr: [1] | [2, 3]): [1, 10] | [2, 3, 10] {
return [...arr, 10];
}
`).noNewErrors(),
]),
test('Non-polymorphic function', [
addCode(`
function foo(arr) {
return [...arr, 1];
}
const ret1 = foo([2]);
const ret2 = foo([3]);
`)
.noNewErrors(),
addCode('(ret1[0]: 2);')
.newErrors(
`
test.js:11
11: (ret1[0]: 2);
^^^^^^^ Cannot cast \`ret1[0]\` to number literal \`2\` because number [1] is incompatible with number literal \`2\` [2]. [incompatible-cast]
References:
8: const ret2 = foo([3]);
^ [1]
11: (ret1[0]: 2);
^ [2]
`,
)
.because('Flow infers the return type to [2,1] | [3,1]'),
addCode('(ret2[0]: 3);')
.newErrors(
`
test.js:13
13: (ret2[0]: 3);
^^^^^^^ Cannot cast \`ret2[0]\` to number literal \`3\` because number [1] is incompatible with number literal \`3\` [2]. [incompatible-cast]
References:
7: const ret1 = foo([2]);
^ [1]
13: (ret2[0]: 3);
^ [2]
`,
)
.because('Flow infers the return type to [2,1] | [3,1]'),
]),
test('Spreading an Array<T> should result in a non-tuple array', [
addCode(`
const tup: Array<number> = [1,2,3];
const nonTup = [...tup];
(nonTup: [1,2,3]);
`).newErrors(
`
test.js:6
6: (nonTup: [1,2,3]);
^^^^^^ Cannot cast \`nonTup\` to tuple type because array literal [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]
References:
5: const nonTup = [...tup];
^^^^^^^^ [1]
6: (nonTup: [1,2,3]);
^^^^^^^ [2]
`,
)
]),
test('Spreading a $ReadOnlyArray should result in a non-tuple array', [
addCode(`
const tup: $ReadOnlyArray<number> = [1,2,3];
const nonTup = [...tup];
(nonTup: [1,2,3]);
`).newErrors(
`
test.js:6
6: (nonTup: [1,2,3]);
^^^^^^ Cannot cast \`nonTup\` to tuple type because array literal [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]
References:
5: const nonTup = [...tup];
^^^^^^^^ [1]
6: (nonTup: [1,2,3]);
^^^^^^^ [2]
`,
)
]),
test('Spreading a string', [
addCode('const arr: Array<number> = [..."hello"];')
.newErrors(
`
test.js:3
3: const arr: Array<number> = [..."hello"];
^^^^^^^^^^^^ Cannot assign array literal to \`arr\` because string [1] is incompatible with number [2] in array element. [incompatible-type]
References:
1083: @@iterator(): Iterator<string>;
^^^^^^ [1]. See lib: [LIB] core.js:1083
3: const arr: Array<number> = [..."hello"];
^^^^^^ [2]
`,
)
.because('String is an Iterable<string>'),
]),
test('Spreading a generator', [
addCode(`
function *foo(): Generator<string, void, void> {
yield "hello";
}
const arr: Array<number> = [...foo()];
`).newErrors(
`
test.js:7
7: const arr: Array<number> = [...foo()];
^^^^^^^^^^ Cannot assign array literal to \`arr\` because string [1] is incompatible with number [2] in array element. [incompatible-type]
References:
4: function *foo(): Generator<string, void, void> {
^^^^^^ [1]
7: const arr: Array<number> = [...foo()];
^^^^^^ [2]
`,
)
.because('Generators are iterables too!'),
]),
test('Spreading an iterator', [
addCode(`
function test(iter: Iterable<string>): Array<number> {
return [...iter];
}
`).newErrors(
`
test.js:5
5: return [...iter];
^^^^^^^^^ Cannot return array literal because string [1] is incompatible with number [2] in array element. [incompatible-return]
References:
4: function test(iter: Iterable<string>): Array<number> {
^^^^^^ [1]
4: function test(iter: Iterable<string>): Array<number> {
^^^^^^ [2]
`,
)
.because('Spec says you can spread iterables')
]),
test('Spreading Object', [
addCode(`
function test(iter: Object): string {
return [...iter];
}
`).noNewErrors()
.because(
'You can spread Object since it might be an Iterable. It is treated ' +
'like spreading any, which results in any'
),
]),
]): Suite);