3282 lines
67 KiB
Plaintext
3282 lines
67 KiB
Plaintext
exports[`test assignment.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function foo(x : ?number) {
|
||
var y;
|
||
if (y = x) {
|
||
var z = y * 1000;
|
||
}
|
||
}
|
||
|
||
type Bar = {
|
||
parent: ?Bar;
|
||
doStuff: () => void
|
||
}
|
||
|
||
function bar0(x : Bar) {
|
||
while (x = x.parent) { // can\'t assign x to ?Bar
|
||
x.doStuff();
|
||
}
|
||
}
|
||
|
||
function bar1(x : ?Bar) {
|
||
while (x = x.parent) { // x.parent might be null
|
||
x.doStuff();
|
||
}
|
||
}
|
||
|
||
function bar2(x : Bar) {
|
||
var y = x;
|
||
while (y = y.parent) {
|
||
y.doStuff();
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function foo(x: ?number) {
|
||
var y;
|
||
if (y = x) {
|
||
var z = y * 1000;
|
||
}
|
||
}
|
||
|
||
type Bar = { parent: ?Bar, doStuff(): void };
|
||
|
||
function bar0(x: Bar) {
|
||
while (x = x.parent) {
|
||
// can\'t assign x to ?Bar
|
||
x.doStuff();
|
||
}
|
||
}
|
||
|
||
function bar1(x: ?Bar) {
|
||
while (x = x.parent) {
|
||
// x.parent might be null
|
||
x.doStuff();
|
||
}
|
||
}
|
||
|
||
function bar2(x: Bar) {
|
||
var y = x;
|
||
while (y = y.parent) {
|
||
y.doStuff();
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test ast_node.js 1`] = `
|
||
"type Node1 = {
|
||
kind: \'Node1\',
|
||
prop1?: string
|
||
};
|
||
|
||
type Node2 = {
|
||
kind: \'Node2\',
|
||
prop2?: string
|
||
}
|
||
|
||
export type ASTNode = Node1 | Node2;
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
type Node1 = { kind: \"Node1\", prop1?: string };
|
||
|
||
type Node2 = { kind: \"Node2\", prop2?: string };
|
||
|
||
export type ASTNode = Node1 | Node2;
|
||
"
|
||
`;
|
||
|
||
exports[`test bool.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function foo(x: ?bool) {
|
||
if (x === false) {
|
||
return;
|
||
}
|
||
|
||
if (x === true) {
|
||
return;
|
||
}
|
||
|
||
x[0]; // error on null and undefined
|
||
}
|
||
|
||
function bar(x: ?bool) {
|
||
if (x !== true) {
|
||
if (x !== false) {
|
||
x[0]; // error on null and undefined
|
||
}
|
||
}
|
||
}
|
||
|
||
function baz(x: ?bool) {
|
||
if (100 * false) {
|
||
return;
|
||
}
|
||
if (false * 100) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
let tests = [
|
||
function(x: { done: true, result: string } | { done: false }) {
|
||
if (x.done === true) {
|
||
return x.result;
|
||
}
|
||
return x.result; // error
|
||
},
|
||
|
||
function(x: { done: true, result: string } | { done: false }) {
|
||
if (true === x.done) {
|
||
return x.result;
|
||
}
|
||
return x.result; // error
|
||
},
|
||
];
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function foo(x: ?boolean) {
|
||
if (x === false) {
|
||
return;
|
||
}
|
||
|
||
if (x === true) {
|
||
return;
|
||
}
|
||
|
||
x[0]; // error on null and undefined
|
||
}
|
||
|
||
function bar(x: ?boolean) {
|
||
if (x !== true) {
|
||
if (x !== false) {
|
||
x[0]; // error on null and undefined
|
||
}
|
||
}
|
||
}
|
||
|
||
function baz(x: ?boolean) {
|
||
if (100 * false) {
|
||
return;
|
||
}
|
||
if (false * 100) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
let tests = [
|
||
function(x: { done: true, result: string } | { done: false }) {
|
||
if (x.done === true) {
|
||
return x.result;
|
||
}
|
||
return x.result; // error
|
||
},
|
||
function(x: { done: true, result: string } | { done: false }) {
|
||
if (true === x.done) {
|
||
return x.result;
|
||
}
|
||
return x.result; // error
|
||
}
|
||
];
|
||
"
|
||
`;
|
||
|
||
exports[`test computed_string_literal.js 1`] = `
|
||
"// @flow
|
||
|
||
type A = {
|
||
\'b_c\': ?string
|
||
};
|
||
|
||
function stuff(str: string) {}
|
||
|
||
function testProperty(a: A) {
|
||
if (a.b_c) {
|
||
stuff(a.b_c)
|
||
}
|
||
}
|
||
|
||
function testLiteralProperty(a: A) {
|
||
if (a[\'b_c\']) {
|
||
stuff(a[\'b_c\'])
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
// @flow
|
||
|
||
type A = { \"b_c\": ?string };
|
||
|
||
function stuff(str: string) {}
|
||
|
||
function testProperty(a: A) {
|
||
if (a.b_c) {
|
||
stuff(a.b_c);
|
||
}
|
||
}
|
||
|
||
function testLiteralProperty(a: A) {
|
||
if (a[\"b_c\"]) {
|
||
stuff(a[\"b_c\"]);
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test cond_prop.js 1`] = `
|
||
"/* @flow */
|
||
|
||
type Type = Name | ListType | NonNullType;
|
||
type Name = {kind: \'Name\', value: string, type: void };
|
||
type ListType = {kind: \'ListType\', type: Type};
|
||
type NonNullType = {kind: \'NonNullType\', type: Name | ListType | BadType};
|
||
type BadType = {};
|
||
|
||
function getTypeASTName(typeAST: Type): string {
|
||
if (!typeAST.type) throw new Error(\'Must be wrapping type\'); // OK
|
||
return getTypeASTName(typeAST.type); // error, BadType not a subtype of Type
|
||
}
|
||
|
||
let tests = [
|
||
function(x: { done: true, result: string } | { done: false }) {
|
||
if (x.done) {
|
||
return x.result;
|
||
}
|
||
return x.result; // error
|
||
},
|
||
|
||
function(x: { done: true, result: string } | { foo: string }) {
|
||
if (x.done) {
|
||
return x.result; // error, consider { foo: \"herp\", done: \"derp\" }
|
||
}
|
||
return x.result; // error
|
||
},
|
||
|
||
function() {
|
||
type T
|
||
= { foo: Object, bar: string }
|
||
| { baz: string, quux: string }
|
||
|
||
function testAlwaysTruthyProp(t: T) {
|
||
if (t.foo) {
|
||
(t.bar: string); // error, consider { baz: \"x\", quux: \"y\", foo: \"boom\" }
|
||
} else {
|
||
(t.quux: string); // ok. since foo is an object (always truthy), the
|
||
// else case completely rules out the first branch of
|
||
// the union.
|
||
}
|
||
}
|
||
|
||
function testSometimesTruthyProp(t: T) {
|
||
if (t.bar) {
|
||
(t.foo: Object); // error, consider { baz: \"x\", quux: \"y\", bar: \"boom\" }
|
||
} else {
|
||
(t.quux: string); // error, consider { foo: {}, bar: \"\" }
|
||
}
|
||
}
|
||
},
|
||
]
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
type Type = Name | ListType | NonNullType;
|
||
type Name = { kind: \"Name\", value: string, type: void };
|
||
type ListType = { kind: \"ListType\", type: Type };
|
||
type NonNullType = { kind: \"NonNullType\", type: Name | ListType | BadType };
|
||
type BadType = {};
|
||
|
||
function getTypeASTName(typeAST: Type): string {
|
||
if (!typeAST.type)
|
||
throw new Error(\"Must be wrapping type\");
|
||
// OK
|
||
return getTypeASTName(typeAST.type); // error, BadType not a subtype of Type
|
||
}
|
||
|
||
let tests = [
|
||
function(x: { done: true, result: string } | { done: false }) {
|
||
if (x.done) {
|
||
return x.result;
|
||
}
|
||
return x.result; // error
|
||
},
|
||
function(x: { done: true, result: string } | { foo: string }) {
|
||
if (x.done) {
|
||
return x.result; // error, consider { foo: \"herp\", done: \"derp\" }
|
||
}
|
||
return x.result; // error
|
||
},
|
||
function() {
|
||
type T = { foo: Object, bar: string } | { baz: string, quux: string };
|
||
|
||
function testAlwaysTruthyProp(t: T) {
|
||
if (t.foo) {
|
||
(t.bar: string); // error, consider { baz: \"x\", quux: \"y\", foo: \"boom\" }
|
||
} else {
|
||
(t.quux: string); // ok. since foo is an object (always truthy), the
|
||
// else case completely rules out the first branch of
|
||
// the union.
|
||
}
|
||
}
|
||
|
||
function testSometimesTruthyProp(t: T) {
|
||
if (t.bar) {
|
||
(t.foo: Object); // error, consider { baz: \"x\", quux: \"y\", bar: \"boom\" }
|
||
} else {
|
||
(t.quux: string); // error, consider { foo: {}, bar: \"\" }
|
||
}
|
||
}
|
||
}
|
||
];
|
||
"
|
||
`;
|
||
|
||
exports[`test constants.js 1`] = `
|
||
"/* @flow */
|
||
|
||
export const SUCCESS: \'SUCCESS\' = \'SUCCESS\';
|
||
export const ERROR: \'ERROR\' = \'ERROR\';
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
export const SUCCESS: \"SUCCESS\" = \"SUCCESS\";
|
||
export const ERROR: \"ERROR\" = \"ERROR\";
|
||
"
|
||
`;
|
||
|
||
exports[`test eq.js 1`] = `
|
||
"/* @flow */
|
||
|
||
let tests = [
|
||
function(x: string, y: number) {
|
||
if (x == y) {} // error, string & number are not comparable (unsafe casting)
|
||
if (x === y) {} // no error, to match \`let z = (x === y)\` which is allowed
|
||
},
|
||
|
||
function(x: string) {
|
||
if (x == undefined) {} // ok
|
||
if (x == void 0) {} // ok
|
||
},
|
||
|
||
function(x: string) {
|
||
if (x == null) {} // ok
|
||
},
|
||
|
||
function(x: { y: \'foo\' } | { y: \'bar\' }) {
|
||
if (x.y == 123) {} // error
|
||
if (x.y === 123) {} // ok
|
||
},
|
||
]
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
let tests = [
|
||
function(x: string, y: number) {
|
||
if (x == y) {}
|
||
// error, string & number are not comparable (unsafe casting)
|
||
if (x === y) {} // no error, to match \`let z = (x === y)\` which is allowed
|
||
},
|
||
function(x: string) {
|
||
if (x == undefined) {}
|
||
// ok
|
||
if (x == void 0) {} // ok
|
||
},
|
||
function(x: string) {
|
||
if (x == null) {} // ok
|
||
},
|
||
function(x: { y: \"foo\" } | { y: \"bar\" }) {
|
||
if (x.y == 123) {}
|
||
// error
|
||
if (x.y === 123) {} // ok
|
||
}
|
||
];
|
||
"
|
||
`;
|
||
|
||
exports[`test exists.js 1`] = `
|
||
"declare class Foo {
|
||
foo: string;
|
||
}
|
||
|
||
function foo0(x: ?string): string {
|
||
return x && x || \"\";
|
||
}
|
||
|
||
function foo1(x: ?Foo): string {
|
||
return x && x.foo || \"\";
|
||
}
|
||
|
||
function foo2(x: ?Class<Foo>): string {
|
||
return x && new x().foo || \"\";
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
declare class Foo { foo: string }
|
||
|
||
function foo0(x: ?string): string {
|
||
return x && x || \"\";
|
||
}
|
||
|
||
function foo1(x: ?Foo): string {
|
||
return x && x.foo || \"\";
|
||
}
|
||
|
||
function foo2(x: ?Class<Foo>): string {
|
||
return x && new x().foo || \"\";
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test func_call.js 1`] = `
|
||
"// @flow
|
||
|
||
let tests = [
|
||
function(x: { y?: string }, z: () => string) {
|
||
if (x.y) {
|
||
// make sure we visit the AST in the correct order. if we visit z() before
|
||
// x.y, then the function call will invalidate the refinement of x.y
|
||
// incorrectly.
|
||
x.y.indexOf(z()); // no error
|
||
}
|
||
},
|
||
];
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
// @flow
|
||
|
||
let tests = [
|
||
function(x: { y?: string }, z: () => string) {
|
||
if (x.y) {
|
||
// make sure we visit the AST in the correct order. if we visit z() before
|
||
// x.y, then the function call will invalidate the refinement of x.y
|
||
// incorrectly.
|
||
x.y.indexOf(z()); // no error
|
||
}
|
||
}
|
||
];
|
||
"
|
||
`;
|
||
|
||
exports[`test hasOwnProperty.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function foo(x:{y?:() => void}) {
|
||
x.y(); // error: could be undefined
|
||
if (x.hasOwnProperty(\'y\')) {
|
||
x.y(); // error: still could be undefined
|
||
}
|
||
if (x.hasOwnProperty(\'z\')) {
|
||
x.z(); // error: unreachable, but we don\'t help you here
|
||
}
|
||
}
|
||
|
||
function bar(x:Object) {
|
||
x.y(); // treated as \`any\`, so allowed
|
||
if (x.hasOwnProperty(\'y\')) {
|
||
x.y(); // still treated as \`any\`, so allowed
|
||
}
|
||
if (x.hasOwnProperty(\'z\')) {
|
||
x.z(); // also treated as \`any\`, so allowed
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function foo(x: { y?: () => void }) {
|
||
x.y();
|
||
// error: could be undefined
|
||
if (x.hasOwnProperty(\"y\")) {
|
||
x.y(); // error: still could be undefined
|
||
}
|
||
if (x.hasOwnProperty(\"z\")) {
|
||
x.z(); // error: unreachable, but we don\'t help you here
|
||
}
|
||
}
|
||
|
||
function bar(x: Object) {
|
||
x.y();
|
||
// treated as \`any\`, so allowed
|
||
if (x.hasOwnProperty(\"y\")) {
|
||
x.y(); // still treated as \`any\`, so allowed
|
||
}
|
||
if (x.hasOwnProperty(\"z\")) {
|
||
x.z(); // also treated as \`any\`, so allowed
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test heap_defassign.js 1`] = `
|
||
"// @flow
|
||
|
||
type Obj = { p: number | string }
|
||
|
||
function f () {}
|
||
|
||
function def_assign_function_havoc(obj: Obj) {
|
||
obj.p = 10; // (obj.p : number)
|
||
f(); // clears refi
|
||
var x: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_setprop_havoc(obj: Obj, obj2: Obj) {
|
||
obj.p = 10; // (obj.p : number)
|
||
obj2.p = \'hey\'; // clears refi
|
||
var x: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_index_havoc(obj: Obj, obj2: Obj) {
|
||
obj.p = 10; // (obj.p : number)
|
||
obj2[\'p\'] = \'hey\'; // clears refi
|
||
var x: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_within_if(b: boolean, obj: Obj) {
|
||
if (b) {
|
||
obj.p = 10; // (obj.p : number)
|
||
var x: number = obj.p // ok by def assign
|
||
}
|
||
var y: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_within_while(b: boolean, obj: Obj) {
|
||
while (b) {
|
||
obj.p = 10; // (obj.p : number)
|
||
var x: number = obj.p // ok by def assign
|
||
}
|
||
var y: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_within_do(b: boolean, obj: Obj) {
|
||
do {
|
||
obj.p = 10; // (obj.p : number)
|
||
var x: number = obj.p // ok by def assign
|
||
} while (b);
|
||
var y: number = obj.p; // no error, loop runs at least once
|
||
}
|
||
|
||
function def_assign_within_try(b: boolean, obj: Obj) {
|
||
obj.p = 10; // (obj.p : number)
|
||
try {
|
||
f(); // clears refi and might throw
|
||
obj.p = \'hey\';
|
||
} catch (e) {
|
||
f(); // clears refi and might throw
|
||
obj.p = \'hey\';
|
||
} finally {
|
||
// NOTE: the values understood to flow to obj.p at this point
|
||
// include the number 42 written downstream;
|
||
// so if we did y:string, we would get at least a spurious error
|
||
// (among other reasonable errors caused by values written upstream)
|
||
var y: number = obj.p; // error, string ~/~ number
|
||
obj.p = 42;
|
||
}
|
||
var z:string = obj.p; // error, number ~/~ string
|
||
}
|
||
|
||
function def_assign_within_for(b: boolean, obj: Obj) {
|
||
for (; b; ) {
|
||
obj.p = 10; // (obj.p : number)
|
||
var x: number = obj.p // ok by def assign
|
||
}
|
||
var z: number = obj.p; // error, (number | string) ~/~ number
|
||
}
|
||
|
||
// --- name-sensitive havoc ---
|
||
|
||
type Obj2 = { q: number | string }
|
||
|
||
function def_assign_setprop_nohavoc(obj: Obj, obj2: Obj2) {
|
||
obj.p = 10; // (obj.p : number)
|
||
obj2.q = \'hey\'; // doesn\'t clear refi of .p
|
||
var x: number = obj.p; // still ok
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
// @flow
|
||
|
||
type Obj = { p: number | string };
|
||
|
||
function f() {}
|
||
|
||
function def_assign_function_havoc(obj: Obj) {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
f();
|
||
// clears refi
|
||
var x: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_setprop_havoc(obj: Obj, obj2: Obj) {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
obj2.p = \"hey\";
|
||
// clears refi
|
||
var x: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_index_havoc(obj: Obj, obj2: Obj) {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
obj2[\"p\"] = \"hey\";
|
||
// clears refi
|
||
var x: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_within_if(b: boolean, obj: Obj) {
|
||
if (b) {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
var x: number = obj.p; // ok by def assign
|
||
}
|
||
var y: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_within_while(b: boolean, obj: Obj) {
|
||
while (b) {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
var x: number = obj.p; // ok by def assign
|
||
}
|
||
var y: number = obj.p; // error, obj.p : number | string
|
||
}
|
||
|
||
function def_assign_within_do(b: boolean, obj: Obj) {
|
||
do {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
var x: number = obj.p; // ok by def assign
|
||
} while (b);
|
||
var y: number = obj.p; // no error, loop runs at least once
|
||
}
|
||
|
||
function def_assign_within_try(b: boolean, obj: Obj) {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
try {
|
||
f();
|
||
// clears refi and might throw
|
||
obj.p = \"hey\";
|
||
} catch (e) {
|
||
f();
|
||
// clears refi and might throw
|
||
obj.p = \"hey\";
|
||
} finally {
|
||
// NOTE: the values understood to flow to obj.p at this point
|
||
// include the number 42 written downstream;
|
||
// so if we did y:string, we would get at least a spurious error
|
||
// (among other reasonable errors caused by values written upstream)
|
||
var y: number = obj.p;
|
||
// error, string ~/~ number
|
||
obj.p = 42;
|
||
}
|
||
var z: string = obj.p; // error, number ~/~ string
|
||
}
|
||
|
||
function def_assign_within_for(b: boolean, obj: Obj) {
|
||
for (; b; ) {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
var x: number = obj.p; // ok by def assign
|
||
}
|
||
var z: number = obj.p; // error, (number | string) ~/~ number
|
||
}
|
||
|
||
// --- name-sensitive havoc ---
|
||
type Obj2 = { q: number | string };
|
||
|
||
function def_assign_setprop_nohavoc(obj: Obj, obj2: Obj2) {
|
||
obj.p = 10;
|
||
// (obj.p : number)
|
||
obj2.q = \"hey\";
|
||
// doesn\'t clear refi of .p
|
||
var x: number = obj.p; // still ok
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test lib.js 1`] = `
|
||
"/* @flow */
|
||
|
||
declare var BAZ: {stuff?: (x: number) => void} | void;
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
declare var BAZ: { stuff?: (x: number) => void } | void;
|
||
"
|
||
`;
|
||
|
||
exports[`test missing-property-cond.js 1`] = `
|
||
"// @flow
|
||
|
||
function foo1(o: { x: number }) {
|
||
if (o.p1) { // OK, this is an idiomatic way of testing property existence
|
||
o.x;
|
||
}
|
||
}
|
||
|
||
function foo2(o: { x: number }) {
|
||
if (o.p2) { // OK
|
||
o.p2.x; // error, since o.p2\'s type is unknown (e.g., could be \`number\`)
|
||
}
|
||
}
|
||
|
||
function foo3(o: { x: number }) {
|
||
o.p3.x; // usual error outside conditional
|
||
}
|
||
|
||
function foo4(o: $Exact<{ x: number }>) {
|
||
if (o.p4) { // OK
|
||
o.p4.x; // currently OK, should be unreachable
|
||
} else {
|
||
o.p4.x; // error
|
||
}
|
||
}
|
||
|
||
function foo5() {
|
||
const o = { };
|
||
_foo5();
|
||
if (o.p) { o.p(); }
|
||
function _foo5() {
|
||
o.p = function() { }
|
||
}
|
||
}
|
||
|
||
function foo6(o: mixed) {
|
||
if (o.bar) {} // error, any lookup on mixed is unsafe
|
||
}
|
||
|
||
function foo7(o: mixed) {
|
||
if (typeof o.bar === \'string\') {} // error
|
||
if (o && typeof o.bar === \'string\') {} // ok
|
||
if (o != null && typeof o.bar === \'string\') {} // ok
|
||
if (o !== null && o !== undefined && typeof o.bar === \'string\') {} // ok
|
||
}
|
||
|
||
function foo8(o: { p: mixed }) {
|
||
if (o.p && o.p.q) {} // this is ok because o.p is truthy, so o.p.q is safe
|
||
if (o.p && o.p.q && o.p.q.r) {}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
// @flow
|
||
|
||
function foo1(o: { x: number }) {
|
||
if (o.p1) {
|
||
// OK, this is an idiomatic way of testing property existence
|
||
o.x;
|
||
}
|
||
}
|
||
|
||
function foo2(o: { x: number }) {
|
||
if (o.p2) {
|
||
// OK
|
||
o.p2.x; // error, since o.p2\'s type is unknown (e.g., could be \`number\`)
|
||
}
|
||
}
|
||
|
||
function foo3(o: { x: number }) {
|
||
o.p3.x; // usual error outside conditional
|
||
}
|
||
|
||
function foo4(o: $Exact<{ x: number }>) {
|
||
if (o.p4) {
|
||
// OK
|
||
o.p4.x; // currently OK, should be unreachable
|
||
} else {
|
||
o.p4.x; // error
|
||
}
|
||
}
|
||
|
||
function foo5() {
|
||
const o = {};
|
||
_foo5();
|
||
if (o.p) {
|
||
o.p();
|
||
}
|
||
function _foo5() {
|
||
o.p = function() {};
|
||
}
|
||
}
|
||
|
||
function foo6(o: mixed) {
|
||
if (o.bar) {} // error, any lookup on mixed is unsafe
|
||
}
|
||
|
||
function foo7(o: mixed) {
|
||
if (typeof o.bar === \"string\") {}
|
||
// error
|
||
if (o && typeof o.bar === \"string\") {}
|
||
// ok
|
||
if (o != null && typeof o.bar === \"string\") {}
|
||
// ok
|
||
if (o !== null && o !== undefined && typeof o.bar === \"string\") {} // ok
|
||
}
|
||
|
||
function foo8(o: { p: mixed }) {
|
||
if (o.p && o.p.q) {}
|
||
// this is ok because o.p is truthy, so o.p.q is safe
|
||
if (o.p && o.p.q && o.p.q.r) {}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test mixed.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function takesNumber(x: number) {}
|
||
function takesString(x: string) {}
|
||
|
||
function num(x: mixed) {
|
||
if (typeof x === \"number\") {
|
||
takesString(x); // error
|
||
(!x: false); // error: we don\'t know the truthiness of x
|
||
}
|
||
if (typeof x === \"number\" && x) {
|
||
(!x: false); // ok
|
||
}
|
||
if (x && typeof x === \"number\") {
|
||
(!x: false); // ok
|
||
}
|
||
}
|
||
|
||
function str(x: mixed) {
|
||
if (typeof x === \"string\") {
|
||
takesNumber(x); // error
|
||
(!x: false); // error: we don\'t know the truthiness of x
|
||
}
|
||
if (typeof x === \"string\" && x) {
|
||
(!x: false); // ok
|
||
}
|
||
if (x && typeof x === \"string\") {
|
||
(!x: false); // ok
|
||
}
|
||
}
|
||
|
||
function bool(x: mixed) {
|
||
if (typeof x === \"boolean\") {
|
||
takesString(x); // error
|
||
(x: true); // error: we don\'t know the truthiness of x
|
||
}
|
||
if (typeof x === \"boolean\" && x) {
|
||
(x: true); // ok
|
||
}
|
||
if (x && typeof x === \"boolean\") {
|
||
(x: true); // ok
|
||
}
|
||
}
|
||
|
||
function fun(x: mixed) {
|
||
if (typeof x === \"function\") {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function obj0(x: mixed) {
|
||
if (typeof x === \"object\") {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function obj1(x: mixed) {
|
||
if (Array.isArray(x)) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function undef(x: mixed) {
|
||
if (typeof x === \"undefined\") {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function null_(x: mixed) {
|
||
if (x === null) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function maybe(x: mixed) {
|
||
if (x == null) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function true_(x: mixed) {
|
||
if (x === true) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function false_(x: mixed) {
|
||
if (x === false) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function obj2(x: mixed) {
|
||
if (typeof x === \"object\") {
|
||
(x: { [key: string]: mixed } | null);
|
||
if (x !== null) {
|
||
(x[\'foo\']: string); // error, mixed
|
||
}
|
||
}
|
||
}
|
||
|
||
function obj2(x: mixed) {
|
||
if (typeof x === \"object\" && x) {
|
||
(x: Object);
|
||
}
|
||
if (x && typeof x === \"object\") {
|
||
(x: Object);
|
||
}
|
||
if (x != null && typeof x === \"object\") {
|
||
(x: Object);
|
||
}
|
||
if (x !== null && typeof x === \"object\") {
|
||
(x: Object);
|
||
}
|
||
}
|
||
|
||
function arr0(x: mixed) {
|
||
if (Array.isArray(x)) {
|
||
takesString(x[0]); // error
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function takesNumber(x: number) {}
|
||
function takesString(x: string) {}
|
||
|
||
function num(x: mixed) {
|
||
if (typeof x === \"number\") {
|
||
takesString(x);
|
||
// error
|
||
(!x: false); // error: we don\'t know the truthiness of x
|
||
}
|
||
if (typeof x === \"number\" && x) {
|
||
(!x: false); // ok
|
||
}
|
||
if (x && typeof x === \"number\") {
|
||
(!x: false); // ok
|
||
}
|
||
}
|
||
|
||
function str(x: mixed) {
|
||
if (typeof x === \"string\") {
|
||
takesNumber(x);
|
||
// error
|
||
(!x: false); // error: we don\'t know the truthiness of x
|
||
}
|
||
if (typeof x === \"string\" && x) {
|
||
(!x: false); // ok
|
||
}
|
||
if (x && typeof x === \"string\") {
|
||
(!x: false); // ok
|
||
}
|
||
}
|
||
|
||
function bool(x: mixed) {
|
||
if (typeof x === \"boolean\") {
|
||
takesString(x);
|
||
// error
|
||
(x: true); // error: we don\'t know the truthiness of x
|
||
}
|
||
if (typeof x === \"boolean\" && x) {
|
||
(x: true); // ok
|
||
}
|
||
if (x && typeof x === \"boolean\") {
|
||
(x: true); // ok
|
||
}
|
||
}
|
||
|
||
function fun(x: mixed) {
|
||
if (typeof x === \"function\") {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function obj0(x: mixed) {
|
||
if (typeof x === \"object\") {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function obj1(x: mixed) {
|
||
if (Array.isArray(x)) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function undef(x: mixed) {
|
||
if (typeof x === \"undefined\") {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function null_(x: mixed) {
|
||
if (x === null) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function maybe(x: mixed) {
|
||
if (x == null) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function true_(x: mixed) {
|
||
if (x === true) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function false_(x: mixed) {
|
||
if (x === false) {
|
||
takesString(x); // error
|
||
}
|
||
}
|
||
|
||
function obj2(x: mixed) {
|
||
if (typeof x === \"object\") {
|
||
(x: { [key: string]: mixed } | null);
|
||
if (x !== null) {
|
||
(x[\"foo\"]: string); // error, mixed
|
||
}
|
||
}
|
||
}
|
||
|
||
function obj2(x: mixed) {
|
||
if (typeof x === \"object\" && x) {
|
||
(x: Object);
|
||
}
|
||
if (x && typeof x === \"object\") {
|
||
(x: Object);
|
||
}
|
||
if (x != null && typeof x === \"object\") {
|
||
(x: Object);
|
||
}
|
||
if (x !== null && typeof x === \"object\") {
|
||
(x: Object);
|
||
}
|
||
}
|
||
|
||
function arr0(x: mixed) {
|
||
if (Array.isArray(x)) {
|
||
takesString(x[0]); // error
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test node1.js 1`] = `
|
||
"module.exports = \'Node1\';
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
module.exports = \"Node1\";
|
||
"
|
||
`;
|
||
|
||
exports[`test not.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function foo(x: ?bool) {
|
||
if (!x) {
|
||
x++; // should error for null, void and bool (false)
|
||
}
|
||
}
|
||
|
||
function bar(x: ?number) {
|
||
if (!x) {
|
||
x[0]; // should error for null, void and number (0)
|
||
}
|
||
}
|
||
|
||
function baz (x: ?number) {
|
||
if (x === null || x === undefined) {
|
||
return;
|
||
}
|
||
|
||
if (!x) {
|
||
x[0]; // should error for number (0)
|
||
}
|
||
}
|
||
|
||
class TestClass {}
|
||
|
||
let tests = [
|
||
function() {
|
||
var y = true;
|
||
while (y) {
|
||
y = !y;
|
||
}
|
||
},
|
||
function(x: Function) {
|
||
(!x: false); // ok, functions are always truthy
|
||
},
|
||
function(x: Object) {
|
||
(!x: false); // ok, objects are always truthy
|
||
},
|
||
function(x: string) {
|
||
(!x: false); // error, strings are not always truthy
|
||
},
|
||
function(x: number) {
|
||
(!x: false); // error, numbers are not always truthy
|
||
},
|
||
function(x: boolean) {
|
||
(!x: false); // error, bools are not always truthy
|
||
},
|
||
function(x: TestClass) {
|
||
(!x: false); // ok, classes are always truthy
|
||
},
|
||
];
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function foo(x: ?boolean) {
|
||
if (!x) {
|
||
x++; // should error for null, void and bool (false)
|
||
}
|
||
}
|
||
|
||
function bar(x: ?number) {
|
||
if (!x) {
|
||
x[0]; // should error for null, void and number (0)
|
||
}
|
||
}
|
||
|
||
function baz(x: ?number) {
|
||
if (x === null || x === undefined) {
|
||
return;
|
||
}
|
||
|
||
if (!x) {
|
||
x[0]; // should error for number (0)
|
||
}
|
||
}
|
||
|
||
class TestClass {}
|
||
|
||
let tests = [
|
||
function() {
|
||
var y = true;
|
||
while (y) {
|
||
y = !y;
|
||
}
|
||
},
|
||
function(x: Function) {
|
||
(!x: false); // ok, functions are always truthy
|
||
},
|
||
function(x: Object) {
|
||
(!x: false); // ok, objects are always truthy
|
||
},
|
||
function(x: string) {
|
||
(!x: false); // error, strings are not always truthy
|
||
},
|
||
function(x: number) {
|
||
(!x: false); // error, numbers are not always truthy
|
||
},
|
||
function(x: boolean) {
|
||
(!x: false); // error, bools are not always truthy
|
||
},
|
||
function(x: TestClass) {
|
||
(!x: false); // ok, classes are always truthy
|
||
}
|
||
];
|
||
"
|
||
`;
|
||
|
||
exports[`test null.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function null_bogus_comparison() {
|
||
if (100 * null) {
|
||
return;
|
||
}
|
||
if (null * 100) {
|
||
return;
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function null_bogus_comparison() {
|
||
if (100 * null) {
|
||
return;
|
||
}
|
||
if (null * 100) {
|
||
return;
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test number.js 1`] = `
|
||
"// @flow
|
||
|
||
type Mode = 0 | 1 | 2;
|
||
|
||
let tests = [
|
||
function(x: number) {
|
||
if (x === 0) {
|
||
(x: void); // error
|
||
}
|
||
(x: 0); // error
|
||
},
|
||
|
||
function(x: number) {
|
||
if (x !== 0) {
|
||
(x: 0); // error
|
||
}
|
||
(x: void); // error
|
||
},
|
||
|
||
function(x: 1): 0 {
|
||
if (x === 0) {
|
||
return x; // unreachable, no error
|
||
}
|
||
return 0;
|
||
},
|
||
|
||
function(x: 0): number {
|
||
if (x === 1) {
|
||
return x;
|
||
}
|
||
return x;
|
||
},
|
||
|
||
function(x: 0) {
|
||
if (x !== 1) {
|
||
(x: 0);
|
||
}
|
||
(x: 0);
|
||
},
|
||
|
||
function(x: 0): number {
|
||
if (x === 0) {
|
||
return x;
|
||
}
|
||
return x;
|
||
},
|
||
|
||
function(x: 0 | 1) {
|
||
if (x === 0) {
|
||
(x: 0);
|
||
(x: void); // error
|
||
}
|
||
if (x === 1) {
|
||
(x: 1);
|
||
(x: void); // error
|
||
}
|
||
},
|
||
|
||
function(x: { foo: number }): 0 {
|
||
if (x.foo === 0) {
|
||
return x.foo;
|
||
}
|
||
return x.foo; // error
|
||
},
|
||
|
||
function(
|
||
x: { kind: 0, foo: number } | { kind: 1, bar: number }
|
||
): number {
|
||
if (x.kind === 0) {
|
||
return x.foo;
|
||
} else {
|
||
return x.bar;
|
||
}
|
||
},
|
||
|
||
function(num: number, obj: { foo: number }) {
|
||
if (num === obj.bar) { // ok, typos allowed in conditionals
|
||
}
|
||
},
|
||
|
||
function(num: number, obj: {[key: string]: number}) {
|
||
if (num === obj.bar) { // ok
|
||
}
|
||
},
|
||
|
||
function(n: number): Mode {
|
||
if (n !== 0 && n !== 1 && n !== 2) {
|
||
throw new Error(\"Wrong number passed\");
|
||
}
|
||
return n;
|
||
},
|
||
|
||
function(s: number): ?Mode {
|
||
if (s === 0) {
|
||
return s;
|
||
} else if (s === 3) {
|
||
return s; // error
|
||
}
|
||
},
|
||
|
||
function(mode: Mode) {
|
||
switch (mode) {
|
||
case 0:
|
||
(mode: 0);
|
||
break;
|
||
|
||
case 1:
|
||
case 2:
|
||
(mode: 1 | 2);
|
||
break;
|
||
}
|
||
},
|
||
|
||
function(x: number): 0 {
|
||
if (x) {
|
||
return x; // error
|
||
} else {
|
||
return x; // no error, inferred to be 0
|
||
}
|
||
},
|
||
];
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
// @flow
|
||
|
||
type Mode = 0 | 1 | 2;
|
||
|
||
let tests = [
|
||
function(x: number) {
|
||
if (x === 0) {
|
||
(x: void); // error
|
||
}
|
||
(x: 0); // error
|
||
},
|
||
function(x: number) {
|
||
if (x !== 0) {
|
||
(x: 0); // error
|
||
}
|
||
(x: void); // error
|
||
},
|
||
function(x: 1): 0 {
|
||
if (x === 0) {
|
||
return x; // unreachable, no error
|
||
}
|
||
return 0;
|
||
},
|
||
function(x: 0): number {
|
||
if (x === 1) {
|
||
return x;
|
||
}
|
||
return x;
|
||
},
|
||
function(x: 0) {
|
||
if (x !== 1) {
|
||
(x: 0);
|
||
}
|
||
(x: 0);
|
||
},
|
||
function(x: 0): number {
|
||
if (x === 0) {
|
||
return x;
|
||
}
|
||
return x;
|
||
},
|
||
function(x: 0 | 1) {
|
||
if (x === 0) {
|
||
(x: 0);
|
||
(x: void); // error
|
||
}
|
||
if (x === 1) {
|
||
(x: 1);
|
||
(x: void); // error
|
||
}
|
||
},
|
||
function(x: { foo: number }): 0 {
|
||
if (x.foo === 0) {
|
||
return x.foo;
|
||
}
|
||
return x.foo; // error
|
||
},
|
||
function(x: { kind: 0, foo: number } | { kind: 1, bar: number }): number {
|
||
if (x.kind === 0) {
|
||
return x.foo;
|
||
} else {
|
||
return x.bar;
|
||
}
|
||
},
|
||
function(num: number, obj: { foo: number }) {
|
||
if (num === obj.bar) {}
|
||
},
|
||
function(num: number, obj: { [key: string]: number }) {
|
||
if (num === obj.bar) {}
|
||
},
|
||
function(n: number): Mode {
|
||
if (n !== 0 && n !== 1 && n !== 2) {
|
||
throw new Error(\"Wrong number passed\");
|
||
}
|
||
return n;
|
||
},
|
||
function(s: number): ?Mode {
|
||
if (s === 0) {
|
||
return s;
|
||
} else if (s === 3) {
|
||
return s; // error
|
||
}
|
||
},
|
||
function(mode: Mode) {
|
||
switch (mode) {
|
||
case 0:
|
||
(mode: 0);
|
||
break;
|
||
|
||
case 1:
|
||
case 2:
|
||
(mode: 1 | 2);
|
||
break;
|
||
}
|
||
},
|
||
function(x: number): 0 {
|
||
if (x) {
|
||
return x; // error
|
||
} else {
|
||
return x; // no error, inferred to be 0
|
||
}
|
||
}
|
||
];
|
||
"
|
||
`;
|
||
|
||
exports[`test property.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function a(x: {[key: string]: ?string}, y: string): string {
|
||
if (x[y]) {
|
||
return x[y];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function b(x: {y: {[key: string]: ?string}}, z: string): string {
|
||
if (x.y[z]) {
|
||
return x.y[z];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function c(x: {[key: string]: ?string}, y: {z: string}): string {
|
||
if (x[y.z]) {
|
||
return x[y.z];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function d(x: {y: {[key: string]: ?string}}, a: {b: string}): string {
|
||
if (x.y[a.b]) {
|
||
return x.y[a.b];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function a_array(x: Array<?string>, y: number): string {
|
||
if (x[y]) {
|
||
return x[y];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function b_array(x: {y: Array<?string>}, z: number): string {
|
||
if (x.y[z]) {
|
||
return x.y[z];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function c_array(x: Array<?string>, y: {z: number}): string {
|
||
if (x[y.z]) {
|
||
return x[y.z];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function d_array(x: {y: Array<?string>}, a: {b: number}): string {
|
||
if (x.y[a.b]) {
|
||
return x.y[a.b];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function e_array(x: Array<?string>): string {
|
||
if (x[0]) {
|
||
return x[0];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
// --- name-sensitive havoc ---
|
||
|
||
function c2(x: {[key: string]: ?string}, y: {z: string}): string {
|
||
if (x[y.z]) {
|
||
y.z = \"HEY\";
|
||
return x[y.z]; // error
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function c3(x: {[key: string]: ?string}, y: {z: string, a: string}): string {
|
||
if (x[y.z]) {
|
||
y.a = \"HEY\";
|
||
return x[y.z]; // ok
|
||
}
|
||
return \"\";
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function a(x: { [key: string]: ?string }, y: string): string {
|
||
if (x[y]) {
|
||
return x[y];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function b(x: { y: { [key: string]: ?string } }, z: string): string {
|
||
if (x.y[z]) {
|
||
return x.y[z];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function c(x: { [key: string]: ?string }, y: { z: string }): string {
|
||
if (x[y.z]) {
|
||
return x[y.z];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function d(x: { y: { [key: string]: ?string } }, a: { b: string }): string {
|
||
if (x.y[a.b]) {
|
||
return x.y[a.b];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function a_array(x: Array<?string>, y: number): string {
|
||
if (x[y]) {
|
||
return x[y];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function b_array(x: { y: Array<?string> }, z: number): string {
|
||
if (x.y[z]) {
|
||
return x.y[z];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function c_array(x: Array<?string>, y: { z: number }): string {
|
||
if (x[y.z]) {
|
||
return x[y.z];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function d_array(x: { y: Array<?string> }, a: { b: number }): string {
|
||
if (x.y[a.b]) {
|
||
return x.y[a.b];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function e_array(x: Array<?string>): string {
|
||
if (x[0]) {
|
||
return x[0];
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
// --- name-sensitive havoc ---
|
||
function c2(x: { [key: string]: ?string }, y: { z: string }): string {
|
||
if (x[y.z]) {
|
||
y.z = \"HEY\";
|
||
return x[y.z]; // error
|
||
}
|
||
return \"\";
|
||
}
|
||
|
||
function c3(
|
||
x: { [key: string]: ?string },
|
||
y: { z: string, a: string }
|
||
): string {
|
||
if (x[y.z]) {
|
||
y.a = \"HEY\";
|
||
return x[y.z]; // ok
|
||
}
|
||
return \"\";
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test refinements.js 1`] = `
|
||
"function foo(b) {
|
||
var x = b? 0 : null;
|
||
while (typeof x == \"string\" || typeof x == \"number\") {
|
||
var y:string = x;
|
||
x = false;
|
||
}
|
||
var z:string = x;
|
||
}
|
||
|
||
function bar(b) {
|
||
var x = b? 0 : null;
|
||
do {
|
||
var y:string = x;
|
||
x = false;
|
||
} while (x === null);
|
||
var z:string = x;
|
||
}
|
||
|
||
function maybe_throw() { }
|
||
function qux() {
|
||
var x = 0;
|
||
try {
|
||
maybe_throw();
|
||
x = \"hello\";
|
||
} catch (e) {
|
||
maybe_throw();
|
||
x = \"hello\";
|
||
} finally {
|
||
// NOTE: the values understood to flow to x at this point
|
||
// include the number 42 written downstream;
|
||
// so if we did y:string, we would get at least a spurious error
|
||
// (among other reasonable errors caused by values written upstream)
|
||
var y:number = x;
|
||
x = 42;
|
||
}
|
||
var z:string = x;
|
||
}
|
||
|
||
function corge(b) {
|
||
for (var x = b? 0 : null;
|
||
typeof x == \"string\" || typeof x == \"number\";
|
||
x = false) {
|
||
var y:string = x;
|
||
}
|
||
var z:string = x;
|
||
}
|
||
|
||
function waldo() {
|
||
var o = {};
|
||
var x = false;
|
||
for (x in o) {
|
||
x = 0; // commenting this out would propagate x:string downstream
|
||
}
|
||
var z:number = x;
|
||
}
|
||
|
||
// regression test: bring a global into scope by testing it.
|
||
// this has no refinement consequences and is error-free.
|
||
// the way we currently cache global lookups causes uneven
|
||
// distribution of the global\'s entries at path merge time,
|
||
// so we need to recognize that it\'s legit rather than an
|
||
// internal error.
|
||
//
|
||
function global_in_conditional0(x: number) {
|
||
// merge_env
|
||
if (x != 0) {
|
||
if (BAZ) {
|
||
}
|
||
}
|
||
}
|
||
|
||
function global_in_conditional2(x: number) {
|
||
// copy_env
|
||
for (var i = 0; i < 100; i++) {
|
||
if (BAZ) {
|
||
}
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
function foo(b) {
|
||
var x = b ? 0 : null;
|
||
while (typeof x == \"string\" || typeof x == \"number\") {
|
||
var y: string = x;
|
||
x = false;
|
||
}
|
||
var z: string = x;
|
||
}
|
||
|
||
function bar(b) {
|
||
var x = b ? 0 : null;
|
||
do {
|
||
var y: string = x;
|
||
x = false;
|
||
} while (x === null);
|
||
var z: string = x;
|
||
}
|
||
|
||
function maybe_throw() {}
|
||
function qux() {
|
||
var x = 0;
|
||
try {
|
||
maybe_throw();
|
||
x = \"hello\";
|
||
} catch (e) {
|
||
maybe_throw();
|
||
x = \"hello\";
|
||
} finally {
|
||
// NOTE: the values understood to flow to x at this point
|
||
// include the number 42 written downstream;
|
||
// so if we did y:string, we would get at least a spurious error
|
||
// (among other reasonable errors caused by values written upstream)
|
||
var y: number = x;
|
||
x = 42;
|
||
}
|
||
var z: string = x;
|
||
}
|
||
|
||
function corge(b) {
|
||
for (
|
||
var x = b ? 0 : null;
|
||
typeof x == \"string\" || typeof x == \"number\";
|
||
x = false
|
||
) {
|
||
var y: string = x;
|
||
}
|
||
var z: string = x;
|
||
}
|
||
|
||
function waldo() {
|
||
var o = {};
|
||
var x = false;
|
||
for (x in o) {
|
||
x = 0; // commenting this out would propagate x:string downstream
|
||
}
|
||
var z: number = x;
|
||
}
|
||
|
||
// regression test: bring a global into scope by testing it.
|
||
// this has no refinement consequences and is error-free.
|
||
// the way we currently cache global lookups causes uneven
|
||
// distribution of the global\'s entries at path merge time,
|
||
// so we need to recognize that it\'s legit rather than an
|
||
// internal error.
|
||
//
|
||
function global_in_conditional0(x: number) {
|
||
// merge_env
|
||
if (x != 0) {
|
||
if (BAZ) {}
|
||
}
|
||
}
|
||
|
||
function global_in_conditional2(x: number) {
|
||
// copy_env
|
||
for (var i = 0; i < 100; i++) {
|
||
if (BAZ) {}
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test string.js 1`] = `
|
||
"// @flow
|
||
|
||
type Mode = \"a\" | \"b\" | \"c\";
|
||
|
||
let tests = [
|
||
function(x: string) {
|
||
if (x === \'foo\') {
|
||
(x: void); // error
|
||
}
|
||
(x: \'foo\'); // error
|
||
},
|
||
|
||
function(x: string) {
|
||
if (x !== \'foo\') {
|
||
(x: \'foo\'); // error
|
||
}
|
||
(x: void); // error
|
||
},
|
||
|
||
function(x: \'bar\'): \'foo\' {
|
||
if (x === \'foo\') {
|
||
return x; // unreachable, no error
|
||
}
|
||
return \'foo\';
|
||
},
|
||
|
||
function(x: \'foo\'): string {
|
||
if (x === \'bar\') {
|
||
return x;
|
||
}
|
||
return x;
|
||
},
|
||
|
||
function(x: \'foo\') {
|
||
if (x !== \'bar\') {
|
||
(x: \'foo\');
|
||
}
|
||
(x: \'foo\');
|
||
},
|
||
|
||
function(x: \'foo\'): string {
|
||
if (x === \'foo\') {
|
||
return x;
|
||
}
|
||
return x;
|
||
},
|
||
|
||
function(x: \'foo\' | \'bar\') {
|
||
if (x === \'foo\') {
|
||
(x: \'foo\');
|
||
(x: void); // error
|
||
}
|
||
if (x === \'bar\') {
|
||
(x: \'bar\');
|
||
(x: void); // error
|
||
}
|
||
},
|
||
|
||
function(x: { foo: string }): \'foo\' {
|
||
if (x.foo === \'foo\') {
|
||
return x.foo;
|
||
}
|
||
return x.foo; // error
|
||
},
|
||
|
||
function(
|
||
x: { kind: \'foo\', foo: string } | { kind: \'bar\', bar: string }
|
||
): string {
|
||
if (x.kind === \'foo\') {
|
||
return x.foo;
|
||
} else {
|
||
return x.bar;
|
||
}
|
||
},
|
||
|
||
function(str: string, obj: { foo: string }) {
|
||
if (str === obj.bar) { // ok, typos allowed in conditionals
|
||
}
|
||
},
|
||
|
||
function(str: string, obj: {[key: string]: string}) {
|
||
if (str === obj.bar) { // ok
|
||
}
|
||
},
|
||
|
||
function(str: string): Mode {
|
||
var ch = str[0];
|
||
if (ch !== \"a\" && ch !== \"b\" && ch !== \"c\") {
|
||
throw new Error(\"Wrong string passed\");
|
||
}
|
||
return ch;
|
||
},
|
||
|
||
function(s: string): ?Mode {
|
||
if (s === \"a\") {
|
||
return s;
|
||
} else if (s === \"d\") {
|
||
return s; // error
|
||
}
|
||
},
|
||
|
||
function(mode: Mode) {
|
||
switch (mode) {
|
||
case \"a\":
|
||
(mode: \"a\");
|
||
break;
|
||
|
||
case \"b\":
|
||
case \"c\":
|
||
(mode: \"b\" | \"c\");
|
||
break;
|
||
}
|
||
},
|
||
|
||
function(x: string): \"\" {
|
||
if (x) {
|
||
return x; // error
|
||
} else {
|
||
return x; // no error, inferred to be \"\"
|
||
}
|
||
},
|
||
|
||
// Simple template literals are ok
|
||
function(x: string): \'foo\' {
|
||
if (x === \`foo\`) {
|
||
return x;
|
||
}
|
||
if (\`foo\` === x) {
|
||
return x;
|
||
}
|
||
return \'foo\';
|
||
},
|
||
];
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
// @flow
|
||
|
||
type Mode = \"a\" | \"b\" | \"c\";
|
||
|
||
let tests = [
|
||
function(x: string) {
|
||
if (x === \"foo\") {
|
||
(x: void); // error
|
||
}
|
||
(x: \"foo\"); // error
|
||
},
|
||
function(x: string) {
|
||
if (x !== \"foo\") {
|
||
(x: \"foo\"); // error
|
||
}
|
||
(x: void); // error
|
||
},
|
||
function(x: \"bar\"): \"foo\" {
|
||
if (x === \"foo\") {
|
||
return x; // unreachable, no error
|
||
}
|
||
return \"foo\";
|
||
},
|
||
function(x: \"foo\"): string {
|
||
if (x === \"bar\") {
|
||
return x;
|
||
}
|
||
return x;
|
||
},
|
||
function(x: \"foo\") {
|
||
if (x !== \"bar\") {
|
||
(x: \"foo\");
|
||
}
|
||
(x: \"foo\");
|
||
},
|
||
function(x: \"foo\"): string {
|
||
if (x === \"foo\") {
|
||
return x;
|
||
}
|
||
return x;
|
||
},
|
||
function(x: \"foo\" | \"bar\") {
|
||
if (x === \"foo\") {
|
||
(x: \"foo\");
|
||
(x: void); // error
|
||
}
|
||
if (x === \"bar\") {
|
||
(x: \"bar\");
|
||
(x: void); // error
|
||
}
|
||
},
|
||
function(x: { foo: string }): \"foo\" {
|
||
if (x.foo === \"foo\") {
|
||
return x.foo;
|
||
}
|
||
return x.foo; // error
|
||
},
|
||
function(
|
||
x: { kind: \"foo\", foo: string } | { kind: \"bar\", bar: string }
|
||
): string {
|
||
if (x.kind === \"foo\") {
|
||
return x.foo;
|
||
} else {
|
||
return x.bar;
|
||
}
|
||
},
|
||
function(str: string, obj: { foo: string }) {
|
||
if (str === obj.bar) {}
|
||
},
|
||
function(str: string, obj: { [key: string]: string }) {
|
||
if (str === obj.bar) {}
|
||
},
|
||
function(str: string): Mode {
|
||
var ch = str[0];
|
||
if (ch !== \"a\" && ch !== \"b\" && ch !== \"c\") {
|
||
throw new Error(\"Wrong string passed\");
|
||
}
|
||
return ch;
|
||
},
|
||
function(s: string): ?Mode {
|
||
if (s === \"a\") {
|
||
return s;
|
||
} else if (s === \"d\") {
|
||
return s; // error
|
||
}
|
||
},
|
||
function(mode: Mode) {
|
||
switch (mode) {
|
||
case \"a\":
|
||
(mode: \"a\");
|
||
break;
|
||
|
||
case \"b\":
|
||
case \"c\":
|
||
(mode: \"b\" | \"c\");
|
||
break;
|
||
}
|
||
},
|
||
function(x: string): \"\" {
|
||
if (x) {
|
||
return x; // error
|
||
} else {
|
||
return x; // no error, inferred to be \"\"
|
||
}
|
||
},
|
||
// Simple template literals are ok
|
||
function(x: string): \"foo\" {
|
||
if (x === \`foo\`) {
|
||
return x;
|
||
}
|
||
if (\`foo\` === x) {
|
||
return x;
|
||
}
|
||
return \"foo\";
|
||
}
|
||
];
|
||
"
|
||
`;
|
||
|
||
exports[`test super_member.js 1`] = `
|
||
"/* @flow */
|
||
|
||
class A {
|
||
prop: string;
|
||
method(): string {
|
||
return \"A\";
|
||
}
|
||
}
|
||
|
||
class B {
|
||
test(): string {
|
||
if (super.prop) { // super.prop doesn\'t exist
|
||
return super.prop; // error, unknown type passed to string expected
|
||
}
|
||
return \"B\";
|
||
}
|
||
}
|
||
|
||
class C extends A {
|
||
test(): string {
|
||
if (super.prop) {
|
||
return super.prop; // OK
|
||
}
|
||
return \"C\";
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
class A {
|
||
prop: string;
|
||
method(): string {
|
||
return \"A\";
|
||
}
|
||
}
|
||
|
||
class B {
|
||
test(): string {
|
||
if (super.prop) {
|
||
// super.prop doesn\'t exist
|
||
return super.prop; // error, unknown type passed to string expected
|
||
}
|
||
return \"B\";
|
||
}
|
||
}
|
||
|
||
class C extends A {
|
||
test(): string {
|
||
if (super.prop) {
|
||
return super.prop; // OK
|
||
}
|
||
return \"C\";
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test switch.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function foo(text: string | number): string {
|
||
switch (typeof text) {
|
||
case \'string\':
|
||
return text;
|
||
case \'number\':
|
||
return text; // error, should return string
|
||
default:
|
||
return \'wat\';
|
||
}
|
||
}
|
||
|
||
function bar(text: string | number): string {
|
||
switch (typeof text) {
|
||
case \'string\':
|
||
return text[0];
|
||
default:
|
||
return (text++) + \'\';
|
||
}
|
||
}
|
||
|
||
function baz1(text: string | number): string {
|
||
switch (typeof text) {
|
||
case \'number\':
|
||
case \'string\':
|
||
return text[0]; // error, [0] on number
|
||
default:
|
||
return \'wat\';
|
||
}
|
||
}
|
||
|
||
function baz2(text: string | number): string {
|
||
switch (typeof text) {
|
||
case \'string\':
|
||
case \'number\':
|
||
return text[0]; // error, [0] on number
|
||
default:
|
||
return \'wat\';
|
||
}
|
||
}
|
||
|
||
function corge(text: string | number | Array<string>): string {
|
||
switch (typeof text) {
|
||
case \'object\':
|
||
return text[0];
|
||
case \'string\':
|
||
case \'number\':
|
||
// using ++ since it isn\'t valid on arrays or strings.
|
||
// should only error for string since Array was filtered out.
|
||
return (text++) + \'\';
|
||
default:
|
||
return \'wat\';
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function foo(text: string | number): string {
|
||
switch (typeof text) {
|
||
case \"string\":
|
||
return text;
|
||
// error, should return string
|
||
case \"number\":
|
||
return text;
|
||
default:
|
||
return \"wat\";
|
||
}
|
||
}
|
||
function bar(text: string | number): string {
|
||
switch (typeof text) {
|
||
case \"string\":
|
||
return text[0];
|
||
default:
|
||
return text++ + \"\";
|
||
}
|
||
}
|
||
function baz1(text: string | number): string {
|
||
switch (typeof text) {
|
||
case \"number\":
|
||
// error, [0] on number
|
||
case \"string\":
|
||
return text[0];
|
||
default:
|
||
return \"wat\";
|
||
}
|
||
}
|
||
function baz2(text: string | number): string {
|
||
switch (typeof text) {
|
||
case \"string\":
|
||
// error, [0] on number
|
||
case \"number\":
|
||
return text[0];
|
||
default:
|
||
return \"wat\";
|
||
}
|
||
}
|
||
function corge(text: string | number | Array<string>): string {
|
||
switch (typeof text) {
|
||
case \"object\":
|
||
return text[0];
|
||
case \"string\":
|
||
case // using ++ since it isn\'t valid on arrays or strings.
|
||
// should only error for string since Array was filtered out.
|
||
\"number\":
|
||
return text++ + \"\";
|
||
|
||
default:
|
||
return \"wat\";
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test tagged_union.js 1`] = `
|
||
"// example 1
|
||
|
||
type Type = Name | ListType;
|
||
type Name = {kind: \'Name\', value: string};
|
||
type ListType = {kind: \'ListType\', name: string};
|
||
|
||
function getTypeASTName(typeAST: Type): string {
|
||
if (typeAST.kind === \'Name\') {
|
||
return typeAST.value; // OK, since typeAST: Name
|
||
} else {
|
||
return typeAST.name; // OK, since typeAST: ListType
|
||
}
|
||
}
|
||
|
||
// example 2
|
||
import type {ASTNode} from \'./ast_node\';
|
||
var Node = require(\'./node1\'); // Node = \"Node1\"
|
||
function foo(x: ASTNode) {
|
||
if (x.kind === Node) {
|
||
return x.prop1.charAt(0); // typeAST: Node1, but x.prop1 may be undefined
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// example 3
|
||
type Apple = { kind: \'Fruit\', taste: \'Bad\' }
|
||
type Orange = { kind: \'Fruit\', taste: \'Good\' }
|
||
type Broccoli = { kind: \'Veg\', taste: \'Bad\', raw: \'No\' }
|
||
type Carrot = { kind: \'Veg\', taste: \'Good\', raw: \'Maybe\' }
|
||
|
||
type Breakfast = Apple | Orange | Broccoli | Carrot
|
||
|
||
function bar(x: Breakfast) {
|
||
if (x.kind === \'Fruit\') { (x.taste: \'Good\'); } // error, Apple.taste = Bad
|
||
else (x.raw: \'No\'); // error, Carrot.raw = Maybe
|
||
}
|
||
|
||
function qux(x: Breakfast) {
|
||
if (x.taste === \'Good\') {
|
||
(x.raw: \'Yes\' | \'No\'); // 2 errors:
|
||
// Orange.raw doesn\'t exist
|
||
// Carrot.raw is neither Yes nor No
|
||
}
|
||
}
|
||
|
||
// example 4
|
||
function list(n) {
|
||
if (n > 0) return { kind: \"cons\", next: list(n-1) };
|
||
return { kind: \"nil\" };
|
||
}
|
||
function length(l) {
|
||
switch (l.kind) {
|
||
case \"cons\": return 1 + length(l.next);
|
||
default: return 0;
|
||
}
|
||
}
|
||
function check(n) {
|
||
if (n >= 0) return (n === (length(list(n))));
|
||
return true;
|
||
}
|
||
|
||
|
||
// example 5
|
||
var EnumKind = { A: 1, B: 2, C: 3};
|
||
type A = { kind: 1, A: number };
|
||
type B = { kind: 2, B: number };
|
||
type C = { kind: 3, C: number };
|
||
function kind(x: A | B | C): number {
|
||
switch (x.kind) {
|
||
case EnumKind.A: return x.A;
|
||
case EnumKind.B: return x.B;
|
||
default: return x.A; // error, x: C and property A not found in type C
|
||
}
|
||
}
|
||
kind({ kind: EnumKind.A, A: 1 });
|
||
|
||
// example 6
|
||
type Citizen = { citizen: true };
|
||
type NonCitizen = { citizen: false, nationality: string }
|
||
function nationality(x: Citizen | NonCitizen) {
|
||
if (x.citizen) return \"Shire\"
|
||
else return x.nationality;
|
||
}
|
||
|
||
let tests = [
|
||
// non-existent props
|
||
function test7(x: A) {
|
||
if (x.kindTypo === 1) { // typos are allowed to be tested
|
||
(x.kindTypo: string); // typos can\'t be used, though
|
||
}
|
||
},
|
||
|
||
// nested objects
|
||
function test8(x: {foo: {bar: 1}}) {
|
||
if (x.foo.bar === 1) {}
|
||
if (x.fooTypo.bar === 1) {} // error, fooTypo doesn\'t exist
|
||
},
|
||
|
||
// invalid RHS
|
||
function(x: A) {
|
||
if (x.kind === (null).toString()) {} // error, method on null
|
||
if ({kind: 1}.kind === (null).toString()) {} // error, method on null
|
||
},
|
||
|
||
// non-objects on LHS
|
||
function(
|
||
x: Array<string>, y: string, z: number, q: boolean,
|
||
r: Object, s: Function, t: () => void
|
||
) {
|
||
if (x.length === 0) {}
|
||
if (x.legnth === 0) { // typos are allowed to be tested
|
||
(x.legnth: number); // inside the block, it\'s a number
|
||
(x.legnth: string); // error: number literal 0 !~> string
|
||
}
|
||
if (y.length === 0) {}
|
||
if (y.legnth === 0) { // typos are allowed to be tested
|
||
(y.legnth: number); // inside the block, it\'s a number
|
||
(y.legnth: string); // error: number literal 0 !~> string
|
||
}
|
||
if (z.toString === 0) {}
|
||
if (z.toStirng === 0) { // typos are allowed to be tested
|
||
(z.toStirng: number); // inside the block, it\'s a number
|
||
(z.toStirng: string); // error: number literal 0 !~> string
|
||
}
|
||
if (q.valueOf === 0) {}
|
||
if (q.valeuOf === 0) { // typos are allowed to be tested
|
||
(q.valeuOf: number); // inside the block, it\'s a number
|
||
(q.valeuOf: string); // error: number literal 0 !~> string
|
||
}
|
||
if (r.toStirng === 0) { // typos are allowed to be tested
|
||
(r.toStirng: empty); // props on AnyObjT are \`any\`
|
||
}
|
||
if (s.call === 0) {}
|
||
if (s.calll === 0) { // typos are allowed to be tested
|
||
(t.calll: empty); // ok, props on functions are \`any\` :/
|
||
}
|
||
if (t.call === 0) {}
|
||
if (t.calll === 0) { // typos are allowed to be tested
|
||
(t.calll: empty); // ok, props on functions are \`any\` :/
|
||
}
|
||
},
|
||
|
||
// sentinel props become the RHS
|
||
function(x: { str: string, num: number, bool: boolean }) {
|
||
if (x.str === \'str\') {
|
||
(x.str: \'not str\'); // error: \'str\' !~> \'not str\'
|
||
}
|
||
if (x.num === 123) {
|
||
(x.num: 456); // error: 123 !~> 456
|
||
}
|
||
if (x.bool === true) {
|
||
(x.bool: false); // error: true !~> false
|
||
}
|
||
// even if it doesn\'t exist...
|
||
if (x.badStr === \'bad\') {
|
||
(x.badStr: empty); // error: \'bad\' !~> empty
|
||
}
|
||
if (x.badNum === 123) {
|
||
(x.badNum: empty); // error: 123 !~> empty
|
||
}
|
||
if (x.badBool === true) {
|
||
(x.badBool: empty); // error: true !~> empty
|
||
}
|
||
},
|
||
|
||
// type mismatch
|
||
function(x: { foo: 123, y: string } | { foo: \'foo\', z: string }) {
|
||
if (x.foo === 123) {
|
||
(x.y: string);
|
||
x.z; // error
|
||
} else {
|
||
(x.z: string);
|
||
x.y; // error
|
||
}
|
||
if (x.foo === \'foo\') {
|
||
(x.z: string);
|
||
x.y; // error
|
||
} else {
|
||
(x.y: string);
|
||
x.z; // error
|
||
}
|
||
},
|
||
|
||
// type mismatch, but one is not a literal
|
||
function(x: { foo: number, y: string } | { foo: \'foo\', z: string }) {
|
||
if (x.foo === 123) {
|
||
(x.y: string); // ok, because 123 !== \'foo\'
|
||
x.z; // error
|
||
} else {
|
||
x.y; // error: x.foo could be a string
|
||
x.z; // error: could still be either case (if foo was a different number)
|
||
}
|
||
|
||
if (x.foo === \'foo\') {
|
||
(x.z: string);
|
||
x.y; // error
|
||
} else {
|
||
(x.y: string);
|
||
x.z; // error
|
||
}
|
||
},
|
||
|
||
// type mismatch, neither is a literal
|
||
function(x: { foo: number, y: string } | { foo: string, z: string }) {
|
||
if (x.foo === 123) {
|
||
(x.y: string); // ok, because 123 !== string
|
||
x.z; // error
|
||
} else {
|
||
x.y; // error: x.foo could be a string
|
||
x.z; // error: could still be either case (if foo was a different number)
|
||
}
|
||
|
||
if (x.foo === \'foo\') {
|
||
(x.z: string);
|
||
x.y; // error
|
||
} else {
|
||
x.y; // error: x.foo could be a different string
|
||
x.z; // error: x.foo could be a number
|
||
}
|
||
},
|
||
|
||
// type mismatch, neither is a literal, test is not a literal either
|
||
function(
|
||
x: { foo: number, y: string } | { foo: string, z: string },
|
||
num: number
|
||
) {
|
||
if (x.foo === num) {
|
||
x.y; // error: flow isn\'t smart enough to figure this out yet
|
||
x.z; // error
|
||
}
|
||
}
|
||
];
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
// example 1
|
||
|
||
type Type = Name | ListType;
|
||
type Name = { kind: \"Name\", value: string };
|
||
type ListType = { kind: \"ListType\", name: string };
|
||
|
||
function getTypeASTName(typeAST: Type): string {
|
||
if (typeAST.kind === \"Name\") {
|
||
return typeAST.value; // OK, since typeAST: Name
|
||
} else {
|
||
return typeAST.name; // OK, since typeAST: ListType
|
||
}
|
||
}
|
||
|
||
// example 2
|
||
import type { ASTNode } from \"./ast_node\";
|
||
var Node = require(\"./node1\");
|
||
// Node = \"Node1\"
|
||
function foo(x: ASTNode) {
|
||
if (x.kind === Node) {
|
||
return x.prop1.charAt(0); // typeAST: Node1, but x.prop1 may be undefined
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// example 3
|
||
type Apple = { kind: \"Fruit\", taste: \"Bad\" };
|
||
type Orange = { kind: \"Fruit\", taste: \"Good\" };
|
||
type Broccoli = { kind: \"Veg\", taste: \"Bad\", raw: \"No\" };
|
||
type Carrot = { kind: \"Veg\", taste: \"Good\", raw: \"Maybe\" };
|
||
|
||
type Breakfast = Apple | Orange | Broccoli | Carrot;
|
||
|
||
function bar(x: Breakfast) {
|
||
if (x.kind === \"Fruit\") {
|
||
(x.taste: \"Good\");
|
||
} // error, Apple.taste = Bad else
|
||
(x.raw: \"No\"); // error, Carrot.raw = Maybe
|
||
}
|
||
|
||
function qux(x: Breakfast) {
|
||
if (x.taste === \"Good\") {
|
||
(x.raw: \"Yes\" | \"No\"); // 2 errors:
|
||
// Orange.raw doesn\'t exist
|
||
// Carrot.raw is neither Yes nor No
|
||
}
|
||
}
|
||
|
||
// example 4
|
||
function list(n) {
|
||
if (n > 0)
|
||
return { kind: \"cons\", next: list(n - 1) };
|
||
return { kind: \"nil\" };
|
||
}
|
||
function length(l) {
|
||
switch (l.kind) {
|
||
case \"cons\":
|
||
return 1 + length(l.next);
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
function check(n) {
|
||
if (n >= 0)
|
||
return n === length(list(n));
|
||
return true;
|
||
}
|
||
|
||
// example 5
|
||
var EnumKind = { A: 1, B: 2, C: 3 };
|
||
type A = { kind: 1, A: number };
|
||
type B = { kind: 2, B: number };
|
||
type C = { kind: 3, C: number };
|
||
function kind(x: A | B | C): number {
|
||
switch (x.kind) {
|
||
case EnumKind.A:
|
||
return x.A;
|
||
case EnumKind.B:
|
||
return x.B;
|
||
// error, x: C and property A not found in type C
|
||
default:
|
||
return x.A;
|
||
}
|
||
}
|
||
kind({ kind: EnumKind.A, A: 1 });
|
||
|
||
// example 6
|
||
type Citizen = { citizen: true };
|
||
type NonCitizen = { citizen: false, nationality: string };
|
||
function nationality(x: Citizen | NonCitizen) {
|
||
if (x.citizen)
|
||
return \"Shire\";
|
||
else
|
||
return x.nationality;
|
||
}
|
||
|
||
let tests = [
|
||
// non-existent props
|
||
function test7(x: A) {
|
||
if (x.kindTypo === 1) {
|
||
// typos are allowed to be tested
|
||
(x.kindTypo: string); // typos can\'t be used, though
|
||
}
|
||
},
|
||
// nested objects
|
||
function test8(x: { foo: { bar: 1 } }) {
|
||
if (x.foo.bar === 1) {}
|
||
if (x.fooTypo.bar === 1) {} // error, fooTypo doesn\'t exist
|
||
},
|
||
// invalid RHS
|
||
function(x: A) {
|
||
if (x.kind === null.toString()) {}
|
||
// error, method on null
|
||
if ({ kind: 1 }.kind === null.toString()) {} // error, method on null
|
||
},
|
||
// non-objects on LHS
|
||
function(
|
||
x: Array<string>,
|
||
y: string,
|
||
z: number,
|
||
q: boolean,
|
||
r: Object,
|
||
s: Function,
|
||
t: () => void
|
||
) {
|
||
if (x.length === 0) {}
|
||
if (x.legnth === 0) {
|
||
// typos are allowed to be tested
|
||
(x.legnth: number);
|
||
// inside the block, it\'s a number
|
||
(x.legnth: string); // error: number literal 0 !~> string
|
||
}
|
||
if (y.length === 0) {}
|
||
if (y.legnth === 0) {
|
||
// typos are allowed to be tested
|
||
(y.legnth: number);
|
||
// inside the block, it\'s a number
|
||
(y.legnth: string); // error: number literal 0 !~> string
|
||
}
|
||
if (z.toString === 0) {}
|
||
if (z.toStirng === 0) {
|
||
// typos are allowed to be tested
|
||
(z.toStirng: number);
|
||
// inside the block, it\'s a number
|
||
(z.toStirng: string); // error: number literal 0 !~> string
|
||
}
|
||
if (q.valueOf === 0) {}
|
||
if (q.valeuOf === 0) {
|
||
// typos are allowed to be tested
|
||
(q.valeuOf: number);
|
||
// inside the block, it\'s a number
|
||
(q.valeuOf: string); // error: number literal 0 !~> string
|
||
}
|
||
if (r.toStirng === 0) {
|
||
// typos are allowed to be tested
|
||
(r.toStirng: empty); // props on AnyObjT are \`any\`
|
||
}
|
||
if (s.call === 0) {}
|
||
if (s.calll === 0) {
|
||
// typos are allowed to be tested
|
||
(t.calll: empty); // ok, props on functions are \`any\` :/
|
||
}
|
||
if (t.call === 0) {}
|
||
if (t.calll === 0) {
|
||
// typos are allowed to be tested
|
||
(t.calll: empty); // ok, props on functions are \`any\` :/
|
||
}
|
||
},
|
||
// sentinel props become the RHS
|
||
function(x: { str: string, num: number, bool: boolean }) {
|
||
if (x.str === \"str\") {
|
||
(x.str: \"not str\"); // error: \'str\' !~> \'not str\'
|
||
}
|
||
if (x.num === 123) {
|
||
(x.num: 456); // error: 123 !~> 456
|
||
}
|
||
if (x.bool === true) {
|
||
(x.bool: false); // error: true !~> false
|
||
}
|
||
// even if it doesn\'t exist...
|
||
if (x.badStr === \"bad\") {
|
||
(x.badStr: empty); // error: \'bad\' !~> empty
|
||
}
|
||
if (x.badNum === 123) {
|
||
(x.badNum: empty); // error: 123 !~> empty
|
||
}
|
||
if (x.badBool === true) {
|
||
(x.badBool: empty); // error: true !~> empty
|
||
}
|
||
},
|
||
// type mismatch
|
||
function(x: { foo: 123, y: string } | { foo: \"foo\", z: string }) {
|
||
if (x.foo === 123) {
|
||
(x.y: string);
|
||
x.z; // error
|
||
} else {
|
||
(x.z: string);
|
||
x.y; // error
|
||
}
|
||
if (x.foo === \"foo\") {
|
||
(x.z: string);
|
||
x.y; // error
|
||
} else {
|
||
(x.y: string);
|
||
x.z; // error
|
||
}
|
||
},
|
||
// type mismatch, but one is not a literal
|
||
function(x: { foo: number, y: string } | { foo: \"foo\", z: string }) {
|
||
if (x.foo === 123) {
|
||
(x.y: string);
|
||
// ok, because 123 !== \'foo\'
|
||
x.z; // error
|
||
} else {
|
||
x.y;
|
||
// error: x.foo could be a string
|
||
x.z; // error: could still be either case (if foo was a different number)
|
||
}
|
||
|
||
if (x.foo === \"foo\") {
|
||
(x.z: string);
|
||
x.y; // error
|
||
} else {
|
||
(x.y: string);
|
||
x.z; // error
|
||
}
|
||
},
|
||
// type mismatch, neither is a literal
|
||
function(x: { foo: number, y: string } | { foo: string, z: string }) {
|
||
if (x.foo === 123) {
|
||
(x.y: string);
|
||
// ok, because 123 !== string
|
||
x.z; // error
|
||
} else {
|
||
x.y;
|
||
// error: x.foo could be a string
|
||
x.z; // error: could still be either case (if foo was a different number)
|
||
}
|
||
|
||
if (x.foo === \"foo\") {
|
||
(x.z: string);
|
||
x.y; // error
|
||
} else {
|
||
x.y;
|
||
// error: x.foo could be a different string
|
||
x.z; // error: x.foo could be a number
|
||
}
|
||
},
|
||
// type mismatch, neither is a literal, test is not a literal either
|
||
function(
|
||
x: { foo: number, y: string } | { foo: string, z: string },
|
||
num: number
|
||
) {
|
||
if (x.foo === num) {
|
||
x.y;
|
||
// error: flow isn\'t smart enough to figure this out yet
|
||
x.z; // error
|
||
}
|
||
}
|
||
];
|
||
"
|
||
`;
|
||
|
||
exports[`test tagged_union_import.js 1`] = `
|
||
"/* @flow */
|
||
|
||
import { SUCCESS, ERROR } from \'./constants\'
|
||
|
||
type Success = {
|
||
type: typeof SUCCESS,
|
||
message: string
|
||
}
|
||
|
||
type Error = {
|
||
type: typeof ERROR,
|
||
error: string
|
||
}
|
||
|
||
function handleStatus(status: Success | Error) {
|
||
switch(status.type) {
|
||
case SUCCESS:
|
||
console.log(\`Successful: \${status.message}\`);
|
||
break;
|
||
default:
|
||
console.log(\`Errored: \${status.error}\`);
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
import { SUCCESS, ERROR } from \"./constants\";
|
||
|
||
type Success = { type: typeof SUCCESS, message: string };
|
||
|
||
type Error = { type: typeof ERROR, error: string };
|
||
|
||
function handleStatus(status: Success | Error) {
|
||
switch (status.type) {
|
||
case SUCCESS:
|
||
console.log(\`Successful: \${status.message}\`);
|
||
break;
|
||
default:
|
||
console.log(\`Errored: \${status.error}\`);
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test typeof.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function foo(x: bool | number) {
|
||
if (typeof x === \"boolean\") {
|
||
x[0]; // error for boolean, not number
|
||
}
|
||
}
|
||
|
||
function bar(): number {
|
||
var x = null;
|
||
if (typeof x === \"object\") {
|
||
return x; // error, null
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* refining globals */
|
||
function fn0() {
|
||
if (typeof BAZ !== \'undefined\' &&
|
||
typeof BAZ.stuff === \'function\') {
|
||
BAZ.stuff(123);
|
||
}
|
||
BAZ.stuff(123); // error, refinement is gone
|
||
}
|
||
function fn1() {
|
||
BAZ.stuff; // error, could be undefined
|
||
if (typeof BAZ !== \'undefined\' &&
|
||
typeof BAZ.stuff === \'function\') {
|
||
BAZ.stuff(123); // ok
|
||
BAZ.stuff(123); // error, refinement is gone
|
||
}
|
||
}
|
||
|
||
function anyfun(x: number | Function): number {
|
||
if (typeof x === \"function\") {
|
||
return 0;
|
||
}
|
||
return x; // OK, x refined to \`number\`
|
||
}
|
||
|
||
function anyobj(x: number | Object): number {
|
||
if (typeof x === \"object\") {
|
||
return 0;
|
||
}
|
||
return x; // OK, x refined to \`number\`
|
||
}
|
||
|
||
function testInvalidValue(x: mixed) {
|
||
if (typeof x === \"foo\") { // error
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
function testTemplateLiteral(x: string | number) {
|
||
if (typeof x === \`string\`) {
|
||
return x.length;
|
||
}
|
||
}
|
||
|
||
function testInvalidTemplateLiteral(x: string | number) {
|
||
if (typeof x === \`foo\`) { // error
|
||
return 0;
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function foo(x: boolean | number) {
|
||
if (typeof x === \"boolean\") {
|
||
x[0]; // error for boolean, not number
|
||
}
|
||
}
|
||
|
||
function bar(): number {
|
||
var x = null;
|
||
if (typeof x === \"object\") {
|
||
return x; // error, null
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* refining globals */
|
||
function fn0() {
|
||
if (typeof BAZ !== \"undefined\" && typeof BAZ.stuff === \"function\") {
|
||
BAZ.stuff(123);
|
||
}
|
||
BAZ.stuff(123); // error, refinement is gone
|
||
}
|
||
function fn1() {
|
||
BAZ.stuff;
|
||
// error, could be undefined
|
||
if (typeof BAZ !== \"undefined\" && typeof BAZ.stuff === \"function\") {
|
||
BAZ.stuff(123);
|
||
// ok
|
||
BAZ.stuff(123); // error, refinement is gone
|
||
}
|
||
}
|
||
|
||
function anyfun(x: number | Function): number {
|
||
if (typeof x === \"function\") {
|
||
return 0;
|
||
}
|
||
return x; // OK, x refined to \`number\`
|
||
}
|
||
|
||
function anyobj(x: number | Object): number {
|
||
if (typeof x === \"object\") {
|
||
return 0;
|
||
}
|
||
return x; // OK, x refined to \`number\`
|
||
}
|
||
|
||
function testInvalidValue(x: mixed) {
|
||
if (typeof x === \"foo\") {
|
||
// error
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
function testTemplateLiteral(x: string | number) {
|
||
if (typeof x === \`string\`) {
|
||
return x.length;
|
||
}
|
||
}
|
||
|
||
function testInvalidTemplateLiteral(x: string | number) {
|
||
if (typeof x === \`foo\`) {
|
||
// error
|
||
return 0;
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test undef.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function undef_var(x: ?number) {
|
||
if (x !== null && x !== undefined) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_var_rev(x: ?number) {
|
||
if (x === null || x === undefined) {
|
||
} else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_prop(x: { x: ?number }) {
|
||
if (x.x !== null && x.x !== undefined) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_prop_rev(x: { x: ?number }) {
|
||
if (x.x === null || x.x === undefined) {
|
||
} else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_var_fail(x: ?number) {
|
||
if (x !== undefined) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_var_fail_rev(x: ?number) {
|
||
if (x === undefined) {
|
||
} else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_prop_fail(x: { x: ?number }) {
|
||
if (x.x !== undefined) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_prop_fail_rev(x: { x: ?number }) {
|
||
if (x.x === undefined) {
|
||
} else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_unreachable(x: number) {
|
||
if (x === undefined) {
|
||
var y = x * 1000; // unreachable
|
||
}
|
||
if (x == undefined) {
|
||
var z = x * 1000; // unreachable
|
||
}
|
||
}
|
||
|
||
function undef_var_nonstrict(x: ?number, y: ?number) {
|
||
if (x != undefined) {
|
||
var a = x * 1000;
|
||
}
|
||
if (y == undefined){
|
||
var b = y * 1000; // error
|
||
}
|
||
}
|
||
|
||
function undef_bogus_comparison() {
|
||
if (100 * undefined) {
|
||
return;
|
||
}
|
||
if (undefined * 100) {
|
||
return;
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function undef_var(x: ?number) {
|
||
if (x !== null && x !== undefined) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_var_rev(x: ?number) {
|
||
if (x === null || x === undefined) {}
|
||
else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_prop(x: { x: ?number }) {
|
||
if (x.x !== null && x.x !== undefined) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_prop_rev(x: { x: ?number }) {
|
||
if (x.x === null || x.x === undefined) {}
|
||
else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_var_fail(x: ?number) {
|
||
if (x !== undefined) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_var_fail_rev(x: ?number) {
|
||
if (x === undefined) {}
|
||
else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_prop_fail(x: { x: ?number }) {
|
||
if (x.x !== undefined) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_prop_fail_rev(x: { x: ?number }) {
|
||
if (x.x === undefined) {}
|
||
else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function undef_unreachable(x: number) {
|
||
if (x === undefined) {
|
||
var y = x * 1000; // unreachable
|
||
}
|
||
if (x == undefined) {
|
||
var z = x * 1000; // unreachable
|
||
}
|
||
}
|
||
|
||
function undef_var_nonstrict(x: ?number, y: ?number) {
|
||
if (x != undefined) {
|
||
var a = x * 1000;
|
||
}
|
||
if (y == undefined) {
|
||
var b = y * 1000; // error
|
||
}
|
||
}
|
||
|
||
function undef_bogus_comparison() {
|
||
if (100 * undefined) {
|
||
return;
|
||
}
|
||
if (undefined * 100) {
|
||
return;
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test union.js 1`] = `
|
||
"/* @flow */
|
||
|
||
type thing = number | bool
|
||
|
||
function foo(x: thing) {
|
||
if (x === true) {
|
||
x[0]; // error on boolean
|
||
}
|
||
}
|
||
|
||
function bar(x: thing) {
|
||
if (x !== true && x !== false) {
|
||
x[0]; // error on number
|
||
}
|
||
}
|
||
|
||
function baz(x: ?thing) {
|
||
if (x && x !== true) {
|
||
x[0]; // error on number
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
type thing = number | boolean;
|
||
|
||
function foo(x: thing) {
|
||
if (x === true) {
|
||
x[0]; // error on boolean
|
||
}
|
||
}
|
||
|
||
function bar(x: thing) {
|
||
if (x !== true && x !== false) {
|
||
x[0]; // error on number
|
||
}
|
||
}
|
||
|
||
function baz(x: ?thing) {
|
||
if (x && x !== true) {
|
||
x[0]; // error on number
|
||
}
|
||
}
|
||
"
|
||
`;
|
||
|
||
exports[`test void.js 1`] = `
|
||
"/* @flow */
|
||
|
||
function void_var(x: ?number) {
|
||
if (x !== null && x !== void(0)) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_rev(x: ?number) {
|
||
if (x === null || x === void(0)) {
|
||
} else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_pro(x: { x: ?number }) {
|
||
if (x.x !== null && x.x !== void(0)) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_pro_rev(x: { x: ?number }) {
|
||
if (x.x === null || x.x === void(0)) {
|
||
} else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_fail(x: ?number) {
|
||
if (x !== void(0)) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_fail_rev(x: ?number) {
|
||
if (x === void(0)) {
|
||
} else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_pro_fail(x: { x: ?number }) {
|
||
if (x.x !== void(0)) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_pro_fail_rev(x: { x: ?number }) {
|
||
if (x.x === void(0)) {
|
||
} else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_side_effect(x: ?number) {
|
||
if (x !== null && x !== void(x * 1000)) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_side_effect_rev(x: ?number) {
|
||
if (x === null || x === void(x * 1000)) {
|
||
} else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_prop_side_effect(x: { x: ?number }) {
|
||
if (x.x !== null && x.x !== void(x.x * 1000)) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_prop_side_effect_rev(x: { x: ?number }) {
|
||
if (x.x === null || x.x === void(x.x * 1000)) {
|
||
} else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_bogus_comparison() {
|
||
if (100 * void(0)) {
|
||
return;
|
||
}
|
||
if (void(0) * 100) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
function void_redefined_undefined(x: ?number) {
|
||
var undefined = \"foo\";
|
||
if (x !== null && x !== void(0)) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
/* @flow */
|
||
|
||
function void_var(x: ?number) {
|
||
if (x !== null && x !== void 0) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_rev(x: ?number) {
|
||
if (x === null || x === void 0) {}
|
||
else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_pro(x: { x: ?number }) {
|
||
if (x.x !== null && x.x !== void 0) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_pro_rev(x: { x: ?number }) {
|
||
if (x.x === null || x.x === void 0) {}
|
||
else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_fail(x: ?number) {
|
||
if (x !== void 0) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_fail_rev(x: ?number) {
|
||
if (x === void 0) {}
|
||
else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_pro_fail(x: { x: ?number }) {
|
||
if (x.x !== void 0) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_pro_fail_rev(x: { x: ?number }) {
|
||
if (x.x === void 0) {}
|
||
else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_side_effect(x: ?number) {
|
||
if (x !== null && x !== void (x * 1000)) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_var_side_effect_rev(x: ?number) {
|
||
if (x === null || x === void (x * 1000)) {}
|
||
else {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_prop_side_effect(x: { x: ?number }) {
|
||
if (x.x !== null && x.x !== void (x.x * 1000)) {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_prop_side_effect_rev(x: { x: ?number }) {
|
||
if (x.x === null || x.x === void (x.x * 1000)) {}
|
||
else {
|
||
var y = x.x * 1000;
|
||
}
|
||
}
|
||
|
||
function void_bogus_comparison() {
|
||
if (100 * void 0) {
|
||
return;
|
||
}
|
||
if (void 0 * 100) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
function void_redefined_undefined(x: ?number) {
|
||
var undefined = \"foo\";
|
||
if (x !== null && x !== void 0) {
|
||
var y = x * 1000;
|
||
}
|
||
}
|
||
"
|
||
`;
|