Fix parsing flow variance on babylon 7 (#1069)
* Enable babylon on variance tests * Fix parsing flow variance in babylon 7
This commit is contained in:
1 parent
dfd42d8306
commit
42910424a1
13 files changed
+1742
-23
No files matched your search
+36
-23
@@ -1361,10 +1361,11 @@ function genericPrintNoParens(path, options, print) {
|
||||
key = concat(["[", path.call(print, "key"), "]"]);
|
||||
} else {
|
||||
key = printPropertyKey(path, options, print);
|
||||
if (n.variance === "plus") {
|
||||
key = concat(["+", key]);
|
||||
} else if (n.variance === "minus") {
|
||||
key = concat(["-", key]);
|
||||
|
||||
var variance = getFlowVariance(n, options);
|
||||
|
||||
if (variance) {
|
||||
key = concat([variance, key]);
|
||||
} else if (n.accessibility === "public") {
|
||||
key = concat(["public ", key]);
|
||||
} else if (n.accessibility === "protected") {
|
||||
@@ -1554,7 +1555,7 @@ function genericPrintNoParens(path, options, print) {
|
||||
// declare function foo(a: B): void; OR
|
||||
// var A: (a: B) => void;
|
||||
var parent = path.getParentNode(0);
|
||||
var isArrowFunctionTypeAnnotation = n.type === "TSFunctionType" || !((!parent.variance &&
|
||||
var isArrowFunctionTypeAnnotation = n.type === "TSFunctionType" || !((!getFlowVariance(parent, options) &&
|
||||
!parent.optional &&
|
||||
namedTypes.ObjectTypeProperty.check(parent)) ||
|
||||
namedTypes.ObjectTypeCallProperty.check(parent) ||
|
||||
@@ -1685,11 +1686,9 @@ function genericPrintNoParens(path, options, print) {
|
||||
|
||||
return concat(parts);
|
||||
case "ObjectTypeIndexer":
|
||||
var variance = n.variance === "plus"
|
||||
? "+"
|
||||
: n.variance === "minus" ? "-" : "";
|
||||
var variance = getFlowVariance(n, options);
|
||||
return concat([
|
||||
variance,
|
||||
variance || "",
|
||||
"[",
|
||||
path.call(print, "id"),
|
||||
n.id ? ": " : "",
|
||||
@@ -1698,12 +1697,10 @@ function genericPrintNoParens(path, options, print) {
|
||||
path.call(print, "value")
|
||||
]);
|
||||
case "ObjectTypeProperty":
|
||||
var variance = n.variance === "plus"
|
||||
? "+"
|
||||
: n.variance === "minus" ? "-" : "";
|
||||
var variance = getFlowVariance(n, options);
|
||||
// TODO: This is a bad hack and we need a better way to know
|
||||
// when to emit an arrow function or not.
|
||||
var isFunction = !n.variance &&
|
||||
var isFunction = !variance &&
|
||||
!n.optional &&
|
||||
n.value.type === "FunctionTypeAnnotation";
|
||||
|
||||
@@ -1713,7 +1710,7 @@ function genericPrintNoParens(path, options, print) {
|
||||
|
||||
return concat([
|
||||
n.static ? "static " : "",
|
||||
variance,
|
||||
variance || "",
|
||||
path.call(print, "key"),
|
||||
n.optional ? "?" : "",
|
||||
isFunction ? "" : ": ",
|
||||
@@ -1789,16 +1786,10 @@ function genericPrintNoParens(path, options, print) {
|
||||
]));
|
||||
}
|
||||
case "TypeParameter":
|
||||
switch (n.variance) {
|
||||
case "plus":
|
||||
parts.push("+");
|
||||
var variance = getFlowVariance(n, options);
|
||||
|
||||
break;
|
||||
case "minus":
|
||||
parts.push("-");
|
||||
|
||||
break;
|
||||
default:
|
||||
if (variance) {
|
||||
parts.push(variance);
|
||||
}
|
||||
|
||||
parts.push(path.call(print, "name"));
|
||||
@@ -2412,6 +2403,28 @@ function printFlowDeclaration(path, parts) {
|
||||
return concat(parts);
|
||||
}
|
||||
|
||||
function getFlowVariance(path, options) {
|
||||
if (!path.variance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Babylon 7.0 currently uses variance node type, and flow should
|
||||
// follow suit soon:
|
||||
// https://github.com/babel/babel/issues/4722
|
||||
const variance = path.variance.kind || path.variance;
|
||||
|
||||
switch (variance) {
|
||||
case "plus":
|
||||
return "+";
|
||||
|
||||
case "minus":
|
||||
return "-";
|
||||
|
||||
default:
|
||||
return variance;
|
||||
}
|
||||
}
|
||||
|
||||
function printClass(path, options, print) {
|
||||
const n = path.getValue();
|
||||
const parts = ["class"];
|
||||
|
||||
@@ -67,3 +67,71 @@ class PropVariance<+Out, -In> {
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test.js 2`] = `
|
||||
"class Variance<+Out,-In> {
|
||||
foo(x: Out): Out { return x; }
|
||||
bar(y: In): In { return y; }
|
||||
}
|
||||
|
||||
class A { }
|
||||
class B extends A { }
|
||||
|
||||
function subtyping(
|
||||
v1: Variance<A,B>,
|
||||
v2: Variance<B,A>
|
||||
) {
|
||||
(v1: Variance<B,A>); // error on both targs (A ~/~> B)
|
||||
(v2: Variance<A,B>); // OK for both targs (B ~> A)
|
||||
}
|
||||
|
||||
class PropVariance<+Out,-In> {
|
||||
inv1: Out; // error
|
||||
inv2: In; // error
|
||||
-co1: Out; // error
|
||||
-co2: In; // ok
|
||||
+con1: Out; // ok
|
||||
+con2: In; // error
|
||||
|
||||
inv_dict1: {[k:string]: Out}; // error
|
||||
inv_dict2: {[k:string]: In}; // error
|
||||
co_dict1: {+[k:string]: Out}; // ok
|
||||
co_dict2: {+[k:string]: In}; // error
|
||||
con_dict1: {-[k:string]: Out}; // error
|
||||
con_dict2: {-[k:string]: In}; // ok
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class Variance<+Out, -In> {
|
||||
foo(x: Out): Out {
|
||||
return x;
|
||||
}
|
||||
bar(y: In): In {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
class A {}
|
||||
class B extends A {}
|
||||
|
||||
function subtyping(v1: Variance<A, B>, v2: Variance<B, A>) {
|
||||
(v1: Variance<B, A>); // error on both targs (A ~/~> B)
|
||||
(v2: Variance<A, B>); // OK for both targs (B ~> A)
|
||||
}
|
||||
|
||||
class PropVariance<+Out, -In> {
|
||||
inv1: Out; // error
|
||||
inv2: In; // error
|
||||
-co1: Out; // error
|
||||
-co2: In; // ok
|
||||
+con1: Out; // ok
|
||||
+con2: In; // error
|
||||
|
||||
inv_dict1: { [k: string]: Out }; // error
|
||||
inv_dict2: { [k: string]: In }; // error
|
||||
co_dict1: { +[k: string]: Out }; // ok
|
||||
co_dict2: { +[k: string]: In }; // error
|
||||
con_dict1: { -[k: string]: Out }; // error
|
||||
con_dict2: { -[k: string]: In }; // ok
|
||||
}
|
||||
"
|
||||
`;
|
||||
@@ -1 +1,2 @@
|
||||
run_spec(__dirname);
|
||||
run_spec(__dirname, { parser: 'babylon' });
|
||||
@@ -15,6 +15,21 @@ const val: string = dict[k]; // error: number incompatible with string
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`any.js 2`] = `
|
||||
"/* @flow */
|
||||
|
||||
const dict: {[key: string]: number} = {}
|
||||
const k: any = 'foo'
|
||||
const val: string = dict[k] // error: number incompatible with string
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* @flow */
|
||||
|
||||
const dict: { [key: string]: number } = {};
|
||||
const k: any = \\"foo\\";
|
||||
const val: string = dict[k]; // error: number incompatible with string
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`compatible.js 1`] = `
|
||||
"/* @flow */
|
||||
|
||||
@@ -52,6 +67,43 @@ function foo2(
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`compatible.js 2`] = `
|
||||
"/* @flow */
|
||||
|
||||
function foo0(x: Array<{[key: string]: mixed}>): Array<{[key: string]: mixed}> {
|
||||
// this adds a fooBar property to the param type, which should NOT cause
|
||||
// an error in the return type because it is a dictionary.
|
||||
x[0].fooBar = 'foobar';
|
||||
return x;
|
||||
}
|
||||
|
||||
function foo2(
|
||||
x: {[key: string]: number}
|
||||
): {[key: string]: number, +toString: () => string} {
|
||||
// x's prototype has a toString method
|
||||
return x;
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* @flow */
|
||||
|
||||
function foo0(
|
||||
x: Array<{ [key: string]: mixed }>
|
||||
): Array<{ [key: string]: mixed }> {
|
||||
// this adds a fooBar property to the param type, which should NOT cause
|
||||
// an error in the return type because it is a dictionary.
|
||||
x[0].fooBar = \\"foobar\\";
|
||||
return x;
|
||||
}
|
||||
|
||||
function foo2(
|
||||
x: { [key: string]: number }
|
||||
): { [key: string]: number, +toString: () => string } {
|
||||
// x's prototype has a toString method
|
||||
return x;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`dictionary.js 1`] = `
|
||||
"/* Dictionary types are object types that include an indexer, which specifies a
|
||||
* key type and a value type. The presence of an indexer makes the object type
|
||||
@@ -652,6 +704,606 @@ function subtype_optional_c_to_dict(x: { p?: C }): { [k: string]: B } {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`dictionary.js 2`] = `
|
||||
"/* Dictionary types are object types that include an indexer, which specifies a
|
||||
* key type and a value type. The presence of an indexer makes the object type
|
||||
* unsealed, but all added properties must be consistent with the indexer
|
||||
* signature.
|
||||
*
|
||||
* Dictionaries can be used to represent the common idiom of objects used as
|
||||
* maps. They can also be used to represent array-like objects, e.g., NodeList
|
||||
* from the DOM API.
|
||||
*
|
||||
* A dictionary is assumed to have every property described by it's key type.
|
||||
* This behavior is similar to the behavior of arrays, which are assumed to have
|
||||
* a value at every index.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// Some logic is variance-sensitive.
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends B {}
|
||||
|
||||
// Just a couple of short type names. Compare to string/number.
|
||||
class X {}
|
||||
class Y {}
|
||||
|
||||
// Any property can be set on a dict with string keys.
|
||||
function set_prop_to_string_key(
|
||||
o: {[k:string]:any},
|
||||
) {
|
||||
o.prop = \\"ok\\";
|
||||
}
|
||||
|
||||
// **UNSOUND**
|
||||
// This is allowed by design. We don't track get/set and we don't wrap the
|
||||
// return type in a maybe.
|
||||
function unsound_dict_has_every_key(
|
||||
o: {[k:string]:X},
|
||||
) {
|
||||
(o.p: X); // ok
|
||||
(o[\\"p\\"]: X); // ok
|
||||
}
|
||||
|
||||
// As with any object type, we can assign subtypes to properties.
|
||||
function set_prop_covariant(
|
||||
o: {[k:string]:B},
|
||||
) {
|
||||
o.p = new A; // error, A ~> B
|
||||
o.p = new B; // ok
|
||||
o.p = new C; // ok
|
||||
}
|
||||
|
||||
// This isn't specific behavior to dictionaries, but for completeness...
|
||||
function get_prop_contravariant(
|
||||
o: {[k:string]:B},
|
||||
) {
|
||||
(o.p: A); // ok
|
||||
(o.p: B); // ok
|
||||
(o.p: C); // error, C ~> B
|
||||
}
|
||||
|
||||
// Dot-notation can not be used to add properties to dictionaries with
|
||||
// non-string keys, because keys are strings.
|
||||
function add_prop_to_nonstring_key_dot(
|
||||
o: {[k:number]:any},
|
||||
) {
|
||||
o.prop = \\"err\\"; // error: string ~> number
|
||||
}
|
||||
|
||||
// Bracket notation can be used to add properties to dictionaries with
|
||||
// non-string keys, even though all keys are strings. This is a convenient
|
||||
// affordance.
|
||||
function add_prop_to_nonstring_key_bracket(
|
||||
o: {[k:number]:any},
|
||||
) {
|
||||
o[0] = \\"ok\\";
|
||||
}
|
||||
|
||||
// Objects can be part dict, part not by mixing an indexer with declared props.
|
||||
function mix_with_declared_props(
|
||||
o: {[k:number]:X,p:Y},
|
||||
x: X,
|
||||
y: Y,
|
||||
) {
|
||||
(o[0]: X); // ok
|
||||
(o.p: Y); // ok
|
||||
o[0] = x; // ok
|
||||
o.p = y; // ok
|
||||
}
|
||||
|
||||
// Indeed, dict types are still Objects and have Object.prototype stuff
|
||||
function object_prototype(
|
||||
o: {[k:string]:number},
|
||||
): {[k:string]:number, +toString: () => string} {
|
||||
(o.toString(): boolean); // error: string ~> boolean
|
||||
return o; // ok
|
||||
}
|
||||
|
||||
// **UNSOUND**
|
||||
// Because we support non-string props w/ bracket notation, it's possible to
|
||||
// write into a declared prop unsoundly.
|
||||
function unsound_string_conversion_alias_declared_prop(
|
||||
o: {[k:number]:any, \\"0\\":X},
|
||||
) {
|
||||
o[0] = \\"not-x\\"; // a[\\"0\\"] no longer X
|
||||
}
|
||||
|
||||
function unification_dict_values_invariant(
|
||||
x: Array<{[k:string]:B}>,
|
||||
) {
|
||||
let a: Array<{[k:string]:A}> = x; // error
|
||||
a[0].p = new A; // in[0].p no longer B
|
||||
|
||||
let b: Array<{[k:string]:B}> = x; // ok
|
||||
|
||||
let c: Array<{[k:string]:C}> = x; // error
|
||||
(x[0].p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_dict_values_invariant(
|
||||
x: {[k:string]:B},
|
||||
) {
|
||||
let a: {[k:string]:A} = x; // error
|
||||
a.p = new A; // x[0].p no longer B
|
||||
|
||||
let b: {[k:string]:B} = x; // ok
|
||||
|
||||
let c: {[k:string]:C} = x; // error
|
||||
(x.p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_dict_values_fresh_exception() {
|
||||
let a: {[k:string]:A} = {
|
||||
a: new A, // ok, A == A
|
||||
b: new B, // ok, B <: A
|
||||
c: new C, // ok, C <: A
|
||||
};
|
||||
|
||||
let b: {[k:string]:B} = {
|
||||
a: new A, // error, A not <: B
|
||||
b: new B, // ok, B == B
|
||||
c: new C, // ok, C <: A
|
||||
};
|
||||
|
||||
let c: {[k:string]:C} = {
|
||||
a: new A, // error, A not <: C
|
||||
b: new B, // error, A not <: C
|
||||
c: new C, // ok, C == C
|
||||
};
|
||||
}
|
||||
|
||||
// Actually, unsound_string_conversion_alias_declared_prop behavior makes an
|
||||
// argument that we shouldn't really care about this, since we ignore the fact
|
||||
// that coercing values to string keys can cause unintended aliasing in general.
|
||||
// Barring some compelling use case for that in this context, though, we choose
|
||||
// to be strict.
|
||||
function unification_dict_keys_invariant(
|
||||
x: Array<{[k:B]:any}>,
|
||||
) {
|
||||
let a: Array<{[k:A]:any}> = x; // error
|
||||
let b: Array<{[k:B]:any}> = x; // ok
|
||||
let c: Array<{[k:C]:any}> = x; // error
|
||||
}
|
||||
|
||||
function subtype_dict_keys_invariant(
|
||||
x: {[k:B]:any},
|
||||
) {
|
||||
let a: {[k:A]:any} = x; // error
|
||||
let b: {[k:B]:any} = x; // ok
|
||||
let c: {[k:C]:any} = x; // error
|
||||
}
|
||||
|
||||
function unification_mix_with_declared_props_invariant_l(
|
||||
x: Array<{[k:string]:B}>,
|
||||
) {
|
||||
let a: Array<{[k:string]:B, p:A}> = x; // error: A ~> B
|
||||
a[0].p = new A; // x[0].p no longer B
|
||||
|
||||
let b: Array<{[k:string]:B, p:B}> = x; // ok
|
||||
|
||||
let c: Array<{[k:string]:B, p:C}> = x; // error
|
||||
(x[0].p: C); // not true
|
||||
}
|
||||
|
||||
function unification_mix_with_declared_props_invariant_r(
|
||||
xa: Array<{[k:string]:A, p:B}>,
|
||||
xb: Array<{[k:string]:B, p:B}>,
|
||||
xc: Array<{[k:string]:C, p:B}>,
|
||||
) {
|
||||
let a: Array<{[k:string]:A}> = xa; // error
|
||||
a[0].p = new A; // xa[0].p no longer B
|
||||
|
||||
let b: Array<{[k:string]:B}> = xb; // ok
|
||||
|
||||
let c: Array<{[k:string]:C}> = xc; // error
|
||||
(xc[0].p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_mix_with_declared_props_invariant_l(
|
||||
x: {[k:string]:B},
|
||||
) {
|
||||
let a: {[k:string]:B, p:A} = x; // error: A ~> B
|
||||
a.p = new A; // x.p no longer B
|
||||
|
||||
let b: {[k:string]:B, p:B} = x; // ok
|
||||
|
||||
let c: {[k:string]:B, p:C} = x; // error
|
||||
(x.p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_mix_with_declared_props_invariant_r(
|
||||
xa: {[k:string]:A, p:B},
|
||||
xb: {[k:string]:B, p:B},
|
||||
xc: {[k:string]:C, p:B},
|
||||
) {
|
||||
let a: {[k:string]:A} = xa; // error
|
||||
a.p = new A; // xa.p no longer B
|
||||
|
||||
let b: {[k:string]:B} = xb; // ok
|
||||
|
||||
let c: {[k:string]:C} = xc; // error
|
||||
(xc.p: C); // not true
|
||||
}
|
||||
|
||||
function unification_dict_to_obj(
|
||||
x: Array<{[k:string]:X}>,
|
||||
): Array<{p:X}> {
|
||||
return x; // error: if allowed, could write {p:X,q:Y} into \`x\`
|
||||
}
|
||||
|
||||
function unification_obj_to_dict(
|
||||
x: Array<{p:X}>,
|
||||
): Array<{[k:string]:X}> {
|
||||
return x; // error: if allowed, could write {p:X,q:Y} into returned array
|
||||
}
|
||||
|
||||
function subtype_dict_to_obj(
|
||||
x: {[k:string]:B},
|
||||
) {
|
||||
let a: {p:A} = x; // error
|
||||
a.p = new A; // x.p no longer B
|
||||
|
||||
let b: {p:B} = x; // ok
|
||||
|
||||
let c: {p:C} = x; // error
|
||||
(x.p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_obj_to_dict(
|
||||
x: {p:B},
|
||||
) {
|
||||
let a: {[k:string]:A} = x; // error
|
||||
a.p = new A; // x.p no longer B
|
||||
|
||||
let b: {[k:string]:B} = x;
|
||||
|
||||
let c: {[k:string]:C} = x; // error
|
||||
(x.p: C); // not true
|
||||
}
|
||||
|
||||
// Only props in l which are not in u must match indexer, but must do so
|
||||
// exactly.
|
||||
function subtype_obj_to_mixed(
|
||||
x: {p:B, x:X},
|
||||
) {
|
||||
let a: {[k:string]:A,x:X} = x; // error (as above), but exclusive of x
|
||||
let b: {[k:string]:B,x:X} = x; // ok,
|
||||
let c: {[k:string]:C,x:X} = x; // error (as above), but exclusive of x
|
||||
}
|
||||
|
||||
function unification_dict_to_mixed(
|
||||
x: Array<{[k:string]:B}>,
|
||||
) {
|
||||
let a: Array<{[k:string]:B, p:A}> = x; // error
|
||||
let b: Array<{[k:string]:B, p:B}> = x; // ok
|
||||
let c: Array<{[k:string]:B, p:C}> = x; // error
|
||||
}
|
||||
|
||||
function subtype_dict_to_mixed(
|
||||
x: {[k:string]:B},
|
||||
) {
|
||||
let a: {[k:string]:B, p:A} = x; // error
|
||||
let b: {[k:string]:B, p:B} = x; // ok
|
||||
let c: {[k:string]:B, p:C} = x; // error
|
||||
}
|
||||
|
||||
function subtype_dict_to_optional_a(
|
||||
x: {[k:string]:B},
|
||||
) {
|
||||
let a: {p?:A} = x; // error
|
||||
}
|
||||
|
||||
function subtype_dict_to_optional_b(
|
||||
x: {[k:string]:B},
|
||||
) {
|
||||
let b: {p?:B} = x; // ok
|
||||
}
|
||||
|
||||
function subtype_dict_to_optional_c(
|
||||
x: {[k:string]:B},
|
||||
) {
|
||||
let c: {p?:C} = x; // error
|
||||
}
|
||||
|
||||
function subtype_optional_a_to_dict(
|
||||
x: {p?:A},
|
||||
): {[k:string]:B} { // error: A ~> B
|
||||
return x;
|
||||
}
|
||||
|
||||
function subtype_optional_b_to_dict(
|
||||
x: {p?:B},
|
||||
): {[k:string]:B} { // ok
|
||||
return x;
|
||||
}
|
||||
|
||||
function subtype_optional_c_to_dict(
|
||||
x: {p?:C},
|
||||
): {[k:string]:B} { // error: C ~> B
|
||||
return x;
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* Dictionary types are object types that include an indexer, which specifies a
|
||||
* key type and a value type. The presence of an indexer makes the object type
|
||||
* unsealed, but all added properties must be consistent with the indexer
|
||||
* signature.
|
||||
*
|
||||
* Dictionaries can be used to represent the common idiom of objects used as
|
||||
* maps. They can also be used to represent array-like objects, e.g., NodeList
|
||||
* from the DOM API.
|
||||
*
|
||||
* A dictionary is assumed to have every property described by it's key type.
|
||||
* This behavior is similar to the behavior of arrays, which are assumed to have
|
||||
* a value at every index.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// Some logic is variance-sensitive.
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends B {}
|
||||
|
||||
// Just a couple of short type names. Compare to string/number.
|
||||
class X {}
|
||||
class Y {}
|
||||
|
||||
// Any property can be set on a dict with string keys.
|
||||
function set_prop_to_string_key(o: { [k: string]: any }) {
|
||||
o.prop = \\"ok\\";
|
||||
}
|
||||
|
||||
// **UNSOUND**
|
||||
// This is allowed by design. We don't track get/set and we don't wrap the
|
||||
// return type in a maybe.
|
||||
function unsound_dict_has_every_key(o: { [k: string]: X }) {
|
||||
(o.p: X); // ok
|
||||
(o[\\"p\\"]: X); // ok
|
||||
}
|
||||
|
||||
// As with any object type, we can assign subtypes to properties.
|
||||
function set_prop_covariant(o: { [k: string]: B }) {
|
||||
o.p = new A(); // error, A ~> B
|
||||
o.p = new B(); // ok
|
||||
o.p = new C(); // ok
|
||||
}
|
||||
|
||||
// This isn't specific behavior to dictionaries, but for completeness...
|
||||
function get_prop_contravariant(o: { [k: string]: B }) {
|
||||
(o.p: A); // ok
|
||||
(o.p: B); // ok
|
||||
(o.p: C); // error, C ~> B
|
||||
}
|
||||
|
||||
// Dot-notation can not be used to add properties to dictionaries with
|
||||
// non-string keys, because keys are strings.
|
||||
function add_prop_to_nonstring_key_dot(o: { [k: number]: any }) {
|
||||
o.prop = \\"err\\"; // error: string ~> number
|
||||
}
|
||||
|
||||
// Bracket notation can be used to add properties to dictionaries with
|
||||
// non-string keys, even though all keys are strings. This is a convenient
|
||||
// affordance.
|
||||
function add_prop_to_nonstring_key_bracket(o: { [k: number]: any }) {
|
||||
o[0] = \\"ok\\";
|
||||
}
|
||||
|
||||
// Objects can be part dict, part not by mixing an indexer with declared props.
|
||||
function mix_with_declared_props(o: { [k: number]: X, p: Y }, x: X, y: Y) {
|
||||
(o[0]: X); // ok
|
||||
(o.p: Y); // ok
|
||||
o[0] = x; // ok
|
||||
o.p = y; // ok
|
||||
}
|
||||
|
||||
// Indeed, dict types are still Objects and have Object.prototype stuff
|
||||
function object_prototype(
|
||||
o: { [k: string]: number }
|
||||
): { [k: string]: number, +toString: () => string } {
|
||||
(o.toString(): boolean); // error: string ~> boolean
|
||||
return o; // ok
|
||||
}
|
||||
|
||||
// **UNSOUND**
|
||||
// Because we support non-string props w/ bracket notation, it's possible to
|
||||
// write into a declared prop unsoundly.
|
||||
function unsound_string_conversion_alias_declared_prop(
|
||||
o: { [k: number]: any, \\"0\\": X }
|
||||
) {
|
||||
o[0] = \\"not-x\\"; // a[\\"0\\"] no longer X
|
||||
}
|
||||
|
||||
function unification_dict_values_invariant(x: Array<{ [k: string]: B }>) {
|
||||
let a: Array<{ [k: string]: A }> = x; // error
|
||||
a[0].p = new A(); // in[0].p no longer B
|
||||
|
||||
let b: Array<{ [k: string]: B }> = x; // ok
|
||||
|
||||
let c: Array<{ [k: string]: C }> = x; // error
|
||||
(x[0].p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_dict_values_invariant(x: { [k: string]: B }) {
|
||||
let a: { [k: string]: A } = x; // error
|
||||
a.p = new A(); // x[0].p no longer B
|
||||
|
||||
let b: { [k: string]: B } = x; // ok
|
||||
|
||||
let c: { [k: string]: C } = x; // error
|
||||
(x.p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_dict_values_fresh_exception() {
|
||||
let a: { [k: string]: A } = {
|
||||
a: new A(), // ok, A == A
|
||||
b: new B(), // ok, B <: A
|
||||
c: new C() // ok, C <: A
|
||||
};
|
||||
|
||||
let b: { [k: string]: B } = {
|
||||
a: new A(), // error, A not <: B
|
||||
b: new B(), // ok, B == B
|
||||
c: new C() // ok, C <: A
|
||||
};
|
||||
|
||||
let c: { [k: string]: C } = {
|
||||
a: new A(), // error, A not <: C
|
||||
b: new B(), // error, A not <: C
|
||||
c: new C() // ok, C == C
|
||||
};
|
||||
}
|
||||
|
||||
// Actually, unsound_string_conversion_alias_declared_prop behavior makes an
|
||||
// argument that we shouldn't really care about this, since we ignore the fact
|
||||
// that coercing values to string keys can cause unintended aliasing in general.
|
||||
// Barring some compelling use case for that in this context, though, we choose
|
||||
// to be strict.
|
||||
function unification_dict_keys_invariant(x: Array<{ [k: B]: any }>) {
|
||||
let a: Array<{ [k: A]: any }> = x; // error
|
||||
let b: Array<{ [k: B]: any }> = x; // ok
|
||||
let c: Array<{ [k: C]: any }> = x; // error
|
||||
}
|
||||
|
||||
function subtype_dict_keys_invariant(x: { [k: B]: any }) {
|
||||
let a: { [k: A]: any } = x; // error
|
||||
let b: { [k: B]: any } = x; // ok
|
||||
let c: { [k: C]: any } = x; // error
|
||||
}
|
||||
|
||||
function unification_mix_with_declared_props_invariant_l(
|
||||
x: Array<{ [k: string]: B }>
|
||||
) {
|
||||
let a: Array<{ [k: string]: B, p: A }> = x; // error: A ~> B
|
||||
a[0].p = new A(); // x[0].p no longer B
|
||||
|
||||
let b: Array<{ [k: string]: B, p: B }> = x; // ok
|
||||
|
||||
let c: Array<{ [k: string]: B, p: C }> = x; // error
|
||||
(x[0].p: C); // not true
|
||||
}
|
||||
|
||||
function unification_mix_with_declared_props_invariant_r(
|
||||
xa: Array<{ [k: string]: A, p: B }>,
|
||||
xb: Array<{ [k: string]: B, p: B }>,
|
||||
xc: Array<{ [k: string]: C, p: B }>
|
||||
) {
|
||||
let a: Array<{ [k: string]: A }> = xa; // error
|
||||
a[0].p = new A(); // xa[0].p no longer B
|
||||
|
||||
let b: Array<{ [k: string]: B }> = xb; // ok
|
||||
|
||||
let c: Array<{ [k: string]: C }> = xc; // error
|
||||
(xc[0].p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_mix_with_declared_props_invariant_l(x: { [k: string]: B }) {
|
||||
let a: { [k: string]: B, p: A } = x; // error: A ~> B
|
||||
a.p = new A(); // x.p no longer B
|
||||
|
||||
let b: { [k: string]: B, p: B } = x; // ok
|
||||
|
||||
let c: { [k: string]: B, p: C } = x; // error
|
||||
(x.p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_mix_with_declared_props_invariant_r(
|
||||
xa: { [k: string]: A, p: B },
|
||||
xb: { [k: string]: B, p: B },
|
||||
xc: { [k: string]: C, p: B }
|
||||
) {
|
||||
let a: { [k: string]: A } = xa; // error
|
||||
a.p = new A(); // xa.p no longer B
|
||||
|
||||
let b: { [k: string]: B } = xb; // ok
|
||||
|
||||
let c: { [k: string]: C } = xc; // error
|
||||
(xc.p: C); // not true
|
||||
}
|
||||
|
||||
function unification_dict_to_obj(
|
||||
x: Array<{ [k: string]: X }>
|
||||
): Array<{ p: X }> {
|
||||
return x; // error: if allowed, could write {p:X,q:Y} into \`x\`
|
||||
}
|
||||
|
||||
function unification_obj_to_dict(
|
||||
x: Array<{ p: X }>
|
||||
): Array<{ [k: string]: X }> {
|
||||
return x; // error: if allowed, could write {p:X,q:Y} into returned array
|
||||
}
|
||||
|
||||
function subtype_dict_to_obj(x: { [k: string]: B }) {
|
||||
let a: { p: A } = x; // error
|
||||
a.p = new A(); // x.p no longer B
|
||||
|
||||
let b: { p: B } = x; // ok
|
||||
|
||||
let c: { p: C } = x; // error
|
||||
(x.p: C); // not true
|
||||
}
|
||||
|
||||
function subtype_obj_to_dict(x: { p: B }) {
|
||||
let a: { [k: string]: A } = x; // error
|
||||
a.p = new A(); // x.p no longer B
|
||||
|
||||
let b: { [k: string]: B } = x;
|
||||
|
||||
let c: { [k: string]: C } = x; // error
|
||||
(x.p: C); // not true
|
||||
}
|
||||
|
||||
// Only props in l which are not in u must match indexer, but must do so
|
||||
// exactly.
|
||||
function subtype_obj_to_mixed(x: { p: B, x: X }) {
|
||||
let a: { [k: string]: A, x: X } = x; // error (as above), but exclusive of x
|
||||
let b: { [k: string]: B, x: X } = x; // ok,
|
||||
let c: { [k: string]: C, x: X } = x; // error (as above), but exclusive of x
|
||||
}
|
||||
|
||||
function unification_dict_to_mixed(x: Array<{ [k: string]: B }>) {
|
||||
let a: Array<{ [k: string]: B, p: A }> = x; // error
|
||||
let b: Array<{ [k: string]: B, p: B }> = x; // ok
|
||||
let c: Array<{ [k: string]: B, p: C }> = x; // error
|
||||
}
|
||||
|
||||
function subtype_dict_to_mixed(x: { [k: string]: B }) {
|
||||
let a: { [k: string]: B, p: A } = x; // error
|
||||
let b: { [k: string]: B, p: B } = x; // ok
|
||||
let c: { [k: string]: B, p: C } = x; // error
|
||||
}
|
||||
|
||||
function subtype_dict_to_optional_a(x: { [k: string]: B }) {
|
||||
let a: { p?: A } = x; // error
|
||||
}
|
||||
|
||||
function subtype_dict_to_optional_b(x: { [k: string]: B }) {
|
||||
let b: { p?: B } = x; // ok
|
||||
}
|
||||
|
||||
function subtype_dict_to_optional_c(x: { [k: string]: B }) {
|
||||
let c: { p?: C } = x; // error
|
||||
}
|
||||
|
||||
function subtype_optional_a_to_dict(x: { p?: A }): { [k: string]: B } {
|
||||
// error: A ~> B
|
||||
return x;
|
||||
}
|
||||
|
||||
function subtype_optional_b_to_dict(x: { p?: B }): { [k: string]: B } {
|
||||
// ok
|
||||
return x;
|
||||
}
|
||||
|
||||
function subtype_optional_c_to_dict(x: { p?: C }): { [k: string]: B } {
|
||||
// error: C ~> B
|
||||
return x;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`incompatible.js 1`] = `
|
||||
"/* @flow */
|
||||
|
||||
@@ -775,6 +1427,129 @@ function foo8(x: { [key: string]: number }) {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`incompatible.js 2`] = `
|
||||
"/* @flow */
|
||||
|
||||
var x : {[key: string]: string} = {};
|
||||
var y : {[key: string]: number} = x; // 2 errors, number !~> string & vice versa
|
||||
var z : {[key: number]: string} = x; // 2 errors, string !~> number & vice versa
|
||||
|
||||
var a : {[key: string]: ?string} = {};
|
||||
var b : {[key: string]: string} = a; // 2 errors (null & undefined)
|
||||
var c : {[key: string]: ?string} = b; // 2 errors, since c['x'] = null updates b
|
||||
|
||||
// 2 errors (number !~> string, string !~> number)
|
||||
function foo0(x: Array<{[key: string]: number}>): Array<{[key: string]: string}> {
|
||||
return x;
|
||||
}
|
||||
|
||||
// error, fooBar:string !~> number (x's dictionary)
|
||||
function foo1(
|
||||
x: Array<{[key: string]: number}>
|
||||
): Array<{[key: string]: number, fooBar: string}> {
|
||||
return x;
|
||||
}
|
||||
|
||||
function foo2(
|
||||
x: Array<{[key: string]: mixed}>
|
||||
): Array<{[key: string]: mixed, fooBar: string}> {
|
||||
x[0].fooBar = 123; // OK, since number ~> mixed (x elem's dictionary)
|
||||
return x; // error: mixed ~> string
|
||||
}
|
||||
|
||||
// OK, since we assume dictionaries have every key
|
||||
function foo3(x: {[key: string]: number}): {foo: number} {
|
||||
return x;
|
||||
}
|
||||
|
||||
// error: foo can't exist in x
|
||||
function foo4(x: {[key: string]: number}): {[key: string]: number, foo: string} {
|
||||
return x;
|
||||
}
|
||||
|
||||
// error, some prop in x could be incompatible (covariance)
|
||||
function foo5(x: Array<{[key: string]: number}>): Array<{foo: number}> {
|
||||
return x;
|
||||
}
|
||||
|
||||
// error, some prop in return could be incompatible
|
||||
function foo6(x: Array<{foo: number}>): Array<{[key: string]: number}> {
|
||||
return x;
|
||||
}
|
||||
|
||||
function foo7(x: {bar: string, [key: string]: number}) {
|
||||
(x.bar: string);
|
||||
}
|
||||
|
||||
function foo8(x: {[key: string]: number}) {
|
||||
(x.foo: string); // error
|
||||
(x.foo: number);
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* @flow */
|
||||
|
||||
var x: { [key: string]: string } = {};
|
||||
var y: { [key: string]: number } = x; // 2 errors, number !~> string & vice versa
|
||||
var z: { [key: number]: string } = x; // 2 errors, string !~> number & vice versa
|
||||
|
||||
var a: { [key: string]: ?string } = {};
|
||||
var b: { [key: string]: string } = a; // 2 errors (null & undefined)
|
||||
var c: { [key: string]: ?string } = b; // 2 errors, since c['x'] = null updates b
|
||||
|
||||
// 2 errors (number !~> string, string !~> number)
|
||||
function foo0(
|
||||
x: Array<{ [key: string]: number }>
|
||||
): Array<{ [key: string]: string }> {
|
||||
return x;
|
||||
}
|
||||
|
||||
// error, fooBar:string !~> number (x's dictionary)
|
||||
function foo1(
|
||||
x: Array<{ [key: string]: number }>
|
||||
): Array<{ [key: string]: number, fooBar: string }> {
|
||||
return x;
|
||||
}
|
||||
|
||||
function foo2(
|
||||
x: Array<{ [key: string]: mixed }>
|
||||
): Array<{ [key: string]: mixed, fooBar: string }> {
|
||||
x[0].fooBar = 123; // OK, since number ~> mixed (x elem's dictionary)
|
||||
return x; // error: mixed ~> string
|
||||
}
|
||||
|
||||
// OK, since we assume dictionaries have every key
|
||||
function foo3(x: { [key: string]: number }): { foo: number } {
|
||||
return x;
|
||||
}
|
||||
|
||||
// error: foo can't exist in x
|
||||
function foo4(
|
||||
x: { [key: string]: number }
|
||||
): { [key: string]: number, foo: string } {
|
||||
return x;
|
||||
}
|
||||
|
||||
// error, some prop in x could be incompatible (covariance)
|
||||
function foo5(x: Array<{ [key: string]: number }>): Array<{ foo: number }> {
|
||||
return x;
|
||||
}
|
||||
|
||||
// error, some prop in return could be incompatible
|
||||
function foo6(x: Array<{ foo: number }>): Array<{ [key: string]: number }> {
|
||||
return x;
|
||||
}
|
||||
|
||||
function foo7(x: { [key: string]: number, bar: string }) {
|
||||
(x.bar: string);
|
||||
}
|
||||
|
||||
function foo8(x: { [key: string]: number }) {
|
||||
(x.foo: string); // error
|
||||
(x.foo: number);
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`issue-1745.js 1`] = `
|
||||
"/* @flow */
|
||||
|
||||
@@ -830,6 +1605,61 @@ class B {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`issue-1745.js 2`] = `
|
||||
"/* @flow */
|
||||
|
||||
class A {
|
||||
x: {[k:string]: number};
|
||||
|
||||
m1() {
|
||||
this.x = { bar: 0 }; // no error
|
||||
}
|
||||
|
||||
m2() {
|
||||
this.x.foo = 0; // no error
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
x: {[k:string]: number};
|
||||
|
||||
m2() {
|
||||
this.x.foo = 0; // no error
|
||||
}
|
||||
|
||||
m1() {
|
||||
this.x = { bar: 0 }; // no error
|
||||
}
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* @flow */
|
||||
|
||||
class A {
|
||||
x: { [k: string]: number };
|
||||
|
||||
m1() {
|
||||
this.x = { bar: 0 }; // no error
|
||||
}
|
||||
|
||||
m2() {
|
||||
this.x.foo = 0; // no error
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
x: { [k: string]: number };
|
||||
|
||||
m2() {
|
||||
this.x.foo = 0; // no error
|
||||
}
|
||||
|
||||
m1() {
|
||||
this.x = { bar: 0 }; // no error
|
||||
}
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test.js 1`] = `
|
||||
"type Params = {count: number; [name: string]: string};
|
||||
type QueryFunction = (params: Params) => string;
|
||||
@@ -855,6 +1685,31 @@ module.exports = o;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test.js 2`] = `
|
||||
"type Params = {count: number; [name: string]: string};
|
||||
type QueryFunction = (params: Params) => string;
|
||||
|
||||
var o: { foo: QueryFunction } = {
|
||||
foo(params) {
|
||||
return params.count; // error, number ~/~ string
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = o;
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
type Params = { [name: string]: string, count: number };
|
||||
type QueryFunction = (params: Params) => string;
|
||||
|
||||
var o: { foo: QueryFunction } = {
|
||||
foo(params) {
|
||||
return params.count; // error, number ~/~ string
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = o;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test_client.js 1`] = `
|
||||
"var o = require('./test');
|
||||
|
||||
@@ -869,3 +1724,18 @@ o.foo = function(params) {
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test_client.js 2`] = `
|
||||
"var o = require('./test');
|
||||
|
||||
o.foo = function (params) {
|
||||
return params.count; // error, number ~/~ string
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
var o = require(\\"./test\\");
|
||||
|
||||
o.foo = function(params) {
|
||||
return params.count; // error, number ~/~ string
|
||||
};
|
||||
"
|
||||
`;
|
||||
@@ -1 +1,2 @@
|
||||
run_spec(__dirname);
|
||||
run_spec(__dirname, { parser: 'babylon' });
|
||||
@@ -133,6 +133,139 @@ foo.propOverriddenWithSetter = 123; // Error number ~> string
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`class.js 2`] = `
|
||||
"/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var z: number = 123;
|
||||
|
||||
class Foo {
|
||||
get goodGetterNoAnnotation() { return 4; }
|
||||
get goodGetterWithAnnotation(): number { return 4; }
|
||||
|
||||
set goodSetterNoAnnotation(x) { z = x; }
|
||||
set goodSetterWithAnnotation(x: number) { z = x; }
|
||||
|
||||
get propWithMatchingGetterAndSetter(): number { return 4; }
|
||||
set propWithMatchingGetterAndSetter(x: number) { }
|
||||
|
||||
// The getter and setter need not have the same type - no error
|
||||
get propWithSubtypingGetterAndSetter(): ?number { return 4; }
|
||||
set propWithSubtypingGetterAndSetter(x: number) { }
|
||||
|
||||
// The getter and setter need not have the same type - no error
|
||||
set propWithSubtypingGetterAndSetterReordered(x: number) { }
|
||||
get propWithSubtypingGetterAndSetterReordered(): ?number { return 4; }
|
||||
|
||||
get propWithMismatchingGetterAndSetter(): number { return 4; }
|
||||
set propWithMismatchingGetterAndSetter(x: string) { } // doesn't match getter (OK)
|
||||
|
||||
propOverriddenWithGetter: number;
|
||||
get propOverriddenWithGetter() { return \\"hello\\"; }
|
||||
|
||||
propOverriddenWithSetter: number;
|
||||
set propOverriddenWithSetter(x: string) { }
|
||||
};
|
||||
|
||||
var foo = new Foo();
|
||||
|
||||
// Test getting properties with getters
|
||||
var testGetterNoError1: number = foo.goodGetterNoAnnotation;
|
||||
var testGetterNoError2: number = foo.goodGetterWithAnnotation;
|
||||
|
||||
var testGetterWithError1: string = foo.goodGetterNoAnnotation; // Error number ~> string
|
||||
var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string
|
||||
|
||||
// Test setting properties with getters
|
||||
foo.goodSetterNoAnnotation = 123;
|
||||
foo.goodSetterWithAnnotation = 123;
|
||||
|
||||
// TODO: Why does no annotation mean no error?
|
||||
foo.goodSetterNoAnnotation = \\"hello\\"; // Error string ~> number
|
||||
foo.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number
|
||||
|
||||
var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number
|
||||
|
||||
var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number
|
||||
foo.propOverriddenWithSetter = 123; // Error number ~> string
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var z: number = 123;
|
||||
|
||||
class Foo {
|
||||
get goodGetterNoAnnotation() {
|
||||
return 4;
|
||||
}
|
||||
get goodGetterWithAnnotation(): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
set goodSetterNoAnnotation(x) {
|
||||
z = x;
|
||||
}
|
||||
set goodSetterWithAnnotation(x: number) {
|
||||
z = x;
|
||||
}
|
||||
|
||||
get propWithMatchingGetterAndSetter(): number {
|
||||
return 4;
|
||||
}
|
||||
set propWithMatchingGetterAndSetter(x: number) {}
|
||||
|
||||
// The getter and setter need not have the same type - no error
|
||||
get propWithSubtypingGetterAndSetter(): ?number {
|
||||
return 4;
|
||||
}
|
||||
set propWithSubtypingGetterAndSetter(x: number) {}
|
||||
|
||||
// The getter and setter need not have the same type - no error
|
||||
set propWithSubtypingGetterAndSetterReordered(x: number) {}
|
||||
get propWithSubtypingGetterAndSetterReordered(): ?number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
get propWithMismatchingGetterAndSetter(): number {
|
||||
return 4;
|
||||
}
|
||||
set propWithMismatchingGetterAndSetter(x: string) {} // doesn't match getter (OK)
|
||||
|
||||
propOverriddenWithGetter: number;
|
||||
get propOverriddenWithGetter() {
|
||||
return \\"hello\\";
|
||||
}
|
||||
|
||||
propOverriddenWithSetter: number;
|
||||
set propOverriddenWithSetter(x: string) {}
|
||||
}
|
||||
|
||||
var foo = new Foo();
|
||||
|
||||
// Test getting properties with getters
|
||||
var testGetterNoError1: number = foo.goodGetterNoAnnotation;
|
||||
var testGetterNoError2: number = foo.goodGetterWithAnnotation;
|
||||
|
||||
var testGetterWithError1: string = foo.goodGetterNoAnnotation; // Error number ~> string
|
||||
var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string
|
||||
|
||||
// Test setting properties with getters
|
||||
foo.goodSetterNoAnnotation = 123;
|
||||
foo.goodSetterWithAnnotation = 123;
|
||||
|
||||
// TODO: Why does no annotation mean no error?
|
||||
foo.goodSetterNoAnnotation = \\"hello\\"; // Error string ~> number
|
||||
foo.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number
|
||||
|
||||
var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number
|
||||
|
||||
var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number
|
||||
foo.propOverriddenWithSetter = 123; // Error number ~> string
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`object.js 1`] = `
|
||||
"/**
|
||||
* @flow
|
||||
@@ -273,6 +406,146 @@ var testExampleOrOrderOfGetterAndSetterReordered: number = obj.exampleOfOrderOfG
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`object.js 2`] = `
|
||||
"/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var z: number = 123;
|
||||
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends A {}
|
||||
|
||||
var obj = {
|
||||
get goodGetterNoAnnotation() { return 4; },
|
||||
get goodGetterWithAnnotation(): number { return 4; },
|
||||
|
||||
set goodSetterNoAnnotation(x) { z = x; },
|
||||
set goodSetterWithAnnotation(x: number) { z = x; },
|
||||
|
||||
get propWithMatchingGetterAndSetter(): number { return 4; },
|
||||
set propWithMatchingGetterAndSetter(x: number) { },
|
||||
|
||||
// The getter and setter need not have the same type
|
||||
get propWithSubtypingGetterAndSetter(): ?number { return 4; }, // OK
|
||||
set propWithSubtypingGetterAndSetter(x: number) { },
|
||||
|
||||
set propWithSubtypingGetterAndSetterReordered(x: number) { }, // OK
|
||||
get propWithSubtypingGetterAndSetterReordered(): ?number { return 4; },
|
||||
|
||||
get exampleOfOrderOfGetterAndSetter(): A { return new A(); },
|
||||
set exampleOfOrderOfGetterAndSetter(x: B) {},
|
||||
|
||||
set exampleOfOrderOfGetterAndSetterReordered(x: B) {},
|
||||
get exampleOfOrderOfGetterAndSetterReordered(): A { return new A(); },
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Test getting properties with getters
|
||||
var testGetterNoError1: number = obj.goodGetterNoAnnotation;
|
||||
var testGetterNoError2: number = obj.goodGetterWithAnnotation;
|
||||
|
||||
var testGetterWithError1: string = obj.goodGetterNoAnnotation; // Error number ~> string
|
||||
var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string
|
||||
|
||||
// Test setting properties with getters
|
||||
obj.goodSetterNoAnnotation = 123;
|
||||
obj.goodSetterWithAnnotation = 123;
|
||||
|
||||
obj.goodSetterNoAnnotation = \\"hello\\"; // Error string ~> number
|
||||
obj.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number
|
||||
|
||||
var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number
|
||||
|
||||
// When building this feature, it was tempting to flow the setter into the
|
||||
// getter and then use either the getter or setter as the type of the property.
|
||||
// This example shows the danger of using the getter's type
|
||||
obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B
|
||||
|
||||
// And this example shows the danger of using the setter's type.
|
||||
var testExampleOrOrderOfGetterAndSetterReordered: number =
|
||||
obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var z: number = 123;
|
||||
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends A {}
|
||||
|
||||
var obj = {
|
||||
get goodGetterNoAnnotation() {
|
||||
return 4;
|
||||
},
|
||||
get goodGetterWithAnnotation(): number {
|
||||
return 4;
|
||||
},
|
||||
|
||||
set goodSetterNoAnnotation(x) {
|
||||
z = x;
|
||||
},
|
||||
set goodSetterWithAnnotation(x: number) {
|
||||
z = x;
|
||||
},
|
||||
|
||||
get propWithMatchingGetterAndSetter(): number {
|
||||
return 4;
|
||||
},
|
||||
set propWithMatchingGetterAndSetter(x: number) {},
|
||||
|
||||
// The getter and setter need not have the same type
|
||||
get propWithSubtypingGetterAndSetter(): ?number {
|
||||
return 4;
|
||||
}, // OK
|
||||
set propWithSubtypingGetterAndSetter(x: number) {},
|
||||
|
||||
set propWithSubtypingGetterAndSetterReordered(x: number) {}, // OK
|
||||
get propWithSubtypingGetterAndSetterReordered(): ?number {
|
||||
return 4;
|
||||
},
|
||||
|
||||
get exampleOfOrderOfGetterAndSetter(): A {
|
||||
return new A();
|
||||
},
|
||||
set exampleOfOrderOfGetterAndSetter(x: B) {},
|
||||
|
||||
set exampleOfOrderOfGetterAndSetterReordered(x: B) {},
|
||||
get exampleOfOrderOfGetterAndSetterReordered(): A {
|
||||
return new A();
|
||||
}
|
||||
};
|
||||
|
||||
// Test getting properties with getters
|
||||
var testGetterNoError1: number = obj.goodGetterNoAnnotation;
|
||||
var testGetterNoError2: number = obj.goodGetterWithAnnotation;
|
||||
|
||||
var testGetterWithError1: string = obj.goodGetterNoAnnotation; // Error number ~> string
|
||||
var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string
|
||||
|
||||
// Test setting properties with getters
|
||||
obj.goodSetterNoAnnotation = 123;
|
||||
obj.goodSetterWithAnnotation = 123;
|
||||
|
||||
obj.goodSetterNoAnnotation = \\"hello\\"; // Error string ~> number
|
||||
obj.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number
|
||||
|
||||
var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number
|
||||
|
||||
// When building this feature, it was tempting to flow the setter into the
|
||||
// getter and then use either the getter or setter as the type of the property.
|
||||
// This example shows the danger of using the getter's type
|
||||
obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B
|
||||
|
||||
// And this example shows the danger of using the setter's type.
|
||||
var testExampleOrOrderOfGetterAndSetterReordered: number = obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`react.js 1`] = `
|
||||
"/**
|
||||
* @flow
|
||||
@@ -304,6 +577,37 @@ React.createClass({
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`react.js 2`] = `
|
||||
"/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
React.createClass({
|
||||
propTypes: {
|
||||
get a() { return 4; },
|
||||
set b(x: number) { this.c = x; },
|
||||
c: 10,
|
||||
}
|
||||
});
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
React.createClass({
|
||||
propTypes: {
|
||||
get a() {
|
||||
return 4;
|
||||
},
|
||||
set b(x: number) {
|
||||
this.c = x;
|
||||
},
|
||||
c: 10
|
||||
}
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`variance.js 1`] = `
|
||||
"/* @flow */
|
||||
|
||||
@@ -464,3 +768,164 @@ class Base {
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`variance.js 2`] = `
|
||||
"/* @flow */
|
||||
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends B {}
|
||||
|
||||
declare var a: A;
|
||||
declare var b: B;
|
||||
declare var c: C;
|
||||
|
||||
class Base {
|
||||
x: B;
|
||||
+pos: B;
|
||||
-neg: B;
|
||||
get get(): B { return this.x };
|
||||
set set(value: B): void { this.x = value };
|
||||
get getset(): B { return this.x };
|
||||
set getset(value: B): void { this.x = value };
|
||||
}
|
||||
|
||||
(class extends Base {
|
||||
// error: getter incompatible with read/write property
|
||||
get x(): B { return b }
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// error: setter incompatible with read/write property
|
||||
set x(value: B): void {}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: get/set co/contra with read/write property, resp.
|
||||
get x(): C { return c }
|
||||
set x(value: A): void {}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// error: setter incompatible with read-only property
|
||||
set pos(value: B): void {}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: getter covariant with read-only property
|
||||
get pos(): C { return c }
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// error: getter incompatible with write-only property
|
||||
get neg(): B { return b }
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: setter contravariant with write-only property
|
||||
set neg(value: A): void {}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: read/write covariant with getter
|
||||
get: C;
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: read/write contravariant with setter
|
||||
set: A;
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: read/write invariant with get/set
|
||||
getset: B;
|
||||
});
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* @flow */
|
||||
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends B {}
|
||||
|
||||
declare var a: A;
|
||||
declare var b: B;
|
||||
declare var c: C;
|
||||
|
||||
class Base {
|
||||
x: B;
|
||||
+pos: B;
|
||||
-neg: B;
|
||||
get get(): B {
|
||||
return this.x;
|
||||
}
|
||||
set set(value: B): void {
|
||||
this.x = value;
|
||||
}
|
||||
get getset(): B {
|
||||
return this.x;
|
||||
}
|
||||
set getset(value: B): void {
|
||||
this.x = value;
|
||||
}
|
||||
}
|
||||
|
||||
(class extends Base {
|
||||
// error: getter incompatible with read/write property
|
||||
get x(): B {
|
||||
return b;
|
||||
}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// error: setter incompatible with read/write property
|
||||
set x(value: B): void {}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: get/set co/contra with read/write property, resp.
|
||||
get x(): C {
|
||||
return c;
|
||||
}
|
||||
set x(value: A): void {}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// error: setter incompatible with read-only property
|
||||
set pos(value: B): void {}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: getter covariant with read-only property
|
||||
get pos(): C {
|
||||
return c;
|
||||
}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// error: getter incompatible with write-only property
|
||||
get neg(): B {
|
||||
return b;
|
||||
}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: setter contravariant with write-only property
|
||||
set neg(value: A): void {}
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: read/write covariant with getter
|
||||
get: C;
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: read/write contravariant with setter
|
||||
set: A;
|
||||
});
|
||||
|
||||
(class extends Base {
|
||||
// ok: read/write invariant with get/set
|
||||
getset: B;
|
||||
});
|
||||
"
|
||||
`;
|
||||
@@ -1 +1,2 @@
|
||||
run_spec(__dirname);
|
||||
run_spec(__dirname, { parser: 'babylon' });
|
||||
@@ -11,6 +11,17 @@ var a: number = o.w.z.y;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`client_object.js 2`] = `
|
||||
"var o = require('./object');
|
||||
|
||||
var a:number = o.w.z.y;
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
var o = require(\\"./object\\");
|
||||
|
||||
var a: number = o.w.z.y;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`object.js 1`] = `
|
||||
"var o1 = { x: 0, y: \\"\\" };
|
||||
var o2 = { z: o1 }
|
||||
@@ -34,6 +45,29 @@ module.exports = o3;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`object.js 2`] = `
|
||||
"var o1 = { x: 0, y: \\"\\" };
|
||||
var o2 = { z: o1 }
|
||||
|
||||
var o3 = {};
|
||||
o3.w = o2;
|
||||
|
||||
//declare var exports: { w: any };
|
||||
|
||||
module.exports = o3;
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
var o1 = { x: 0, y: \\"\\" };
|
||||
var o2 = { z: o1 };
|
||||
|
||||
var o3 = {};
|
||||
o3.w = o2;
|
||||
|
||||
//declare var exports: { w: any };
|
||||
|
||||
module.exports = o3;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`proto.js 1`] = `
|
||||
"function Foo() { this.x = 0; }
|
||||
Foo.prototype.m = function() { }
|
||||
@@ -53,6 +87,25 @@ var o2: Foo = new Foo();
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`proto.js 2`] = `
|
||||
"function Foo() { this.x = 0; }
|
||||
Foo.prototype.m = function() { }
|
||||
|
||||
var o1: { x: number; m(): void } = new Foo();
|
||||
|
||||
var o2: Foo = new Foo();
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
function Foo() {
|
||||
this.x = 0;
|
||||
}
|
||||
Foo.prototype.m = function() {};
|
||||
|
||||
var o1: { x: number, m(): void } = new Foo();
|
||||
|
||||
var o2: Foo = new Foo();
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`super.js 1`] = `
|
||||
"class C { m() { } }
|
||||
class D extends C { }
|
||||
@@ -67,3 +120,18 @@ class D extends C {}
|
||||
var d: { +m: () => void } = new D();
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`super.js 2`] = `
|
||||
"class C { m() { } }
|
||||
class D extends C { }
|
||||
|
||||
var d: { +m: () => void } = new D();
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class C {
|
||||
m() {}
|
||||
}
|
||||
class D extends C {}
|
||||
|
||||
var d: { +m: () => void } = new D();
|
||||
"
|
||||
`;
|
||||
@@ -1 +1,2 @@
|
||||
run_spec(__dirname);
|
||||
run_spec(__dirname, { parser: 'babylon' });
|
||||
@@ -11,6 +11,17 @@ module.exports = id;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`id.js 2`] = `
|
||||
"declare function id<X>(_: X): X;
|
||||
|
||||
module.exports = id;
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
declare function id<X>(_: X): X;
|
||||
|
||||
module.exports = id;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`subtype.js 1`] = `
|
||||
"interface Interface {
|
||||
m(): void;
|
||||
@@ -30,6 +41,25 @@ function subtypeCheck(x: Interface): ObjectType {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`subtype.js 2`] = `
|
||||
"interface Interface {
|
||||
m(): void;
|
||||
}
|
||||
import type { ObjectType } from './test';
|
||||
|
||||
function subtypeCheck(x: Interface): ObjectType { return x; }
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
interface Interface {
|
||||
m(): void
|
||||
}
|
||||
import type { ObjectType } from \\"./test\\";
|
||||
|
||||
function subtypeCheck(x: Interface): ObjectType {
|
||||
return x;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test.js 1`] = `
|
||||
"const id = require('./id');
|
||||
|
||||
@@ -59,6 +89,35 @@ module.exports = id(methodCaller);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test.js 2`] = `
|
||||
"const id = require('./id');
|
||||
|
||||
export type ObjectType = {
|
||||
+m: () => void,
|
||||
};
|
||||
|
||||
function methodCaller(x: ObjectType) {
|
||||
x.m();
|
||||
};
|
||||
|
||||
module.exports = id(
|
||||
methodCaller
|
||||
);
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
const id = require(\\"./id\\");
|
||||
|
||||
export type ObjectType = {
|
||||
+m: () => void
|
||||
};
|
||||
|
||||
function methodCaller(x: ObjectType) {
|
||||
x.m();
|
||||
}
|
||||
|
||||
module.exports = id(methodCaller);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test2.js 1`] = `
|
||||
"/* @flow */
|
||||
|
||||
@@ -98,6 +157,45 @@ b.f(); // error, property \`p\` not found
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test2.js 2`] = `
|
||||
"/* @flow */
|
||||
|
||||
function f() {
|
||||
return this.p;
|
||||
}
|
||||
|
||||
var a = {
|
||||
p: 0,
|
||||
f
|
||||
}
|
||||
|
||||
var b = {
|
||||
f
|
||||
}
|
||||
|
||||
a.f(); // okey-dokie
|
||||
b.f(); // error, property \`p\` not found
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* @flow */
|
||||
|
||||
function f() {
|
||||
return this.p;
|
||||
}
|
||||
|
||||
var a = {
|
||||
p: 0,
|
||||
f
|
||||
};
|
||||
|
||||
var b = {
|
||||
f
|
||||
};
|
||||
|
||||
a.f(); // okey-dokie
|
||||
b.f(); // error, property \`p\` not found
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test3.js 1`] = `
|
||||
"/* @flow */
|
||||
|
||||
@@ -138,3 +236,44 @@ function qux(o: { f: () => void }) {
|
||||
qux({ f: foo }); // error, since \`this\` is used non-trivially in \`foo\`
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test3.js 2`] = `
|
||||
"/* @flow */
|
||||
|
||||
function foo() {
|
||||
this.m();
|
||||
}
|
||||
|
||||
function bar(f: () => void) {
|
||||
f(); // passing global object as \`this\`
|
||||
({ f }).f(); // passing container object as \`this\`
|
||||
}
|
||||
|
||||
bar(foo); // error, since \`this\` is used non-trivially in \`foo\`
|
||||
|
||||
function qux(o: { f: () => void }) {
|
||||
o.f(); // passing o as \`this\`
|
||||
}
|
||||
|
||||
qux({ f: foo }); // error, since \`this\` is used non-trivially in \`foo\`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* @flow */
|
||||
|
||||
function foo() {
|
||||
this.m();
|
||||
}
|
||||
|
||||
function bar(f: () => void) {
|
||||
f(); // passing global object as \`this\`
|
||||
({ f }.f()); // passing container object as \`this\`
|
||||
}
|
||||
|
||||
bar(foo); // error, since \`this\` is used non-trivially in \`foo\`
|
||||
|
||||
function qux(o: { f: () => void }) {
|
||||
o.f(); // passing o as \`this\`
|
||||
}
|
||||
|
||||
qux({ f: foo }); // error, since \`this\` is used non-trivially in \`foo\`
|
||||
"
|
||||
`;
|
||||
@@ -1 +1,2 @@
|
||||
run_spec(__dirname);
|
||||
run_spec(__dirname, { parser: 'babylon' });
|
||||
@@ -45,6 +45,51 @@ declare var b: B<any>;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test1.js 2`] = `
|
||||
"/* This test ensures that the code below does not take a long time to check. If
|
||||
* this test is taking a very long time to complete, there is a bug. */
|
||||
|
||||
class A {}
|
||||
|
||||
type B<T> = A & {
|
||||
+a: () => B<T>;
|
||||
+b: () => B<T>;
|
||||
+c: () => B<T>;
|
||||
+d: () => B<T>;
|
||||
+e: () => B<T>;
|
||||
+f: () => B<T>;
|
||||
+g: () => B<T>;
|
||||
+h: () => B<T>;
|
||||
+i: () => B<T>;
|
||||
};
|
||||
|
||||
declare var b: B<any>;
|
||||
|
||||
(b: B<any>);
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* This test ensures that the code below does not take a long time to check. If
|
||||
* this test is taking a very long time to complete, there is a bug. */
|
||||
|
||||
class A {}
|
||||
|
||||
type B<T> = A & {
|
||||
+a: () => B<T>,
|
||||
+b: () => B<T>,
|
||||
+c: () => B<T>,
|
||||
+d: () => B<T>,
|
||||
+e: () => B<T>,
|
||||
+f: () => B<T>,
|
||||
+g: () => B<T>,
|
||||
+h: () => B<T>,
|
||||
+i: () => B<T>
|
||||
};
|
||||
|
||||
declare var b: B<any>;
|
||||
|
||||
(b: B<any>);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test2.js 1`] = `
|
||||
"/* This test ensures that the code below does not take a long time to check. If
|
||||
* this test is taking a very long time to complete, there is a bug. */
|
||||
@@ -89,3 +134,48 @@ declare var b: B<any>;
|
||||
(b: B<any>);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`test2.js 2`] = `
|
||||
"/* This test ensures that the code below does not take a long time to check. If
|
||||
* this test is taking a very long time to complete, there is a bug. */
|
||||
|
||||
class A {}
|
||||
|
||||
type B<T> = A & {
|
||||
+a: (x: B<T>) => void;
|
||||
+b: (x: B<T>) => void;
|
||||
+c: (x: B<T>) => void;
|
||||
+d: (x: B<T>) => void;
|
||||
+e: (x: B<T>) => void;
|
||||
+f: (x: B<T>) => void;
|
||||
+g: (x: B<T>) => void;
|
||||
+h: (x: B<T>) => void;
|
||||
+i: (x: B<T>) => void;
|
||||
};
|
||||
|
||||
declare var b: B<any>;
|
||||
|
||||
(b: B<any>);
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* This test ensures that the code below does not take a long time to check. If
|
||||
* this test is taking a very long time to complete, there is a bug. */
|
||||
|
||||
class A {}
|
||||
|
||||
type B<T> = A & {
|
||||
+a: (x: B<T>) => void,
|
||||
+b: (x: B<T>) => void,
|
||||
+c: (x: B<T>) => void,
|
||||
+d: (x: B<T>) => void,
|
||||
+e: (x: B<T>) => void,
|
||||
+f: (x: B<T>) => void,
|
||||
+g: (x: B<T>) => void,
|
||||
+h: (x: B<T>) => void,
|
||||
+i: (x: B<T>) => void
|
||||
};
|
||||
|
||||
declare var b: B<any>;
|
||||
|
||||
(b: B<any>);
|
||||
"
|
||||
`;
|
||||
@@ -1 +1,2 @@
|
||||
run_spec(__dirname);
|
||||
run_spec(__dirname, { parser: 'babylon' });
|
||||
Reference in new issue
Block a user