2017-02-22 00:59:19 +03:00
|
|
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
|
|
|
2017-03-22 03:38:28 +03:00
|
|
|
exports[`any.js 1`] = `
|
2017-04-12 23:41:51 +03:00
|
|
|
/* @flow */
|
2016-12-23 22:31:38 +03:00
|
|
|
|
|
|
|
const dict: {[key: string]: number} = {}
|
2017-02-22 00:59:19 +03:00
|
|
|
const k: any = 'foo'
|
2016-12-23 22:31:38 +03:00
|
|
|
const val: string = dict[k] // error: number incompatible with string
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
/* @flow */
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
const dict: { [key: string]: number } = {};
|
2017-04-12 23:41:51 +03:00
|
|
|
const k: any = "foo";
|
2017-01-11 18:16:38 +03:00
|
|
|
const val: string = dict[k]; // error: number incompatible with string
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
`;
|
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
exports[`compatible.js 1`] = `
|
2017-04-12 23:41:51 +03:00
|
|
|
/* @flow */
|
2017-03-22 19:37:26 +03:00
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
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;
|
2017-03-22 19:37:26 +03:00
|
|
|
}
|
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
function foo2(
|
|
|
|
x: {[key: string]: number}
|
|
|
|
): {[key: string]: number, +toString: () => string} {
|
|
|
|
// x's prototype has a toString method
|
2016-12-23 22:31:38 +03:00
|
|
|
return x;
|
|
|
|
}
|
2017-03-25 18:10:17 +03:00
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
/* @flow */
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
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.
|
2017-04-12 23:41:51 +03:00
|
|
|
x[0].fooBar = "foobar";
|
2016-12-27 21:29:31 +03:00
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-05-02 05:12:53 +03:00
|
|
|
function foo2(x: {
|
|
|
|
[key: string]: number
|
|
|
|
}): { [key: string]: number, +toString: () => string } {
|
2017-03-25 18:10:17 +03:00
|
|
|
// x's prototype has a toString method
|
2016-12-27 21:29:31 +03:00
|
|
|
return x;
|
2017-01-11 18:16:38 +03:00
|
|
|
}
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
`;
|
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
exports[`dictionary.js 1`] = `
|
2017-04-12 23:41:51 +03:00
|
|
|
/* Dictionary types are object types that include an indexer, which specifies a
|
2016-12-23 22:31:38 +03:00
|
|
|
* 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.
|
|
|
|
*
|
2017-02-22 00:59:19 +03:00
|
|
|
* A dictionary is assumed to have every property described by it's key type.
|
2016-12-23 22:31:38 +03:00
|
|
|
* 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},
|
|
|
|
) {
|
2017-04-12 23:41:51 +03:00
|
|
|
o.prop = "ok";
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// **UNSOUND**
|
2017-02-22 00:59:19 +03:00
|
|
|
// This is allowed by design. We don't track get/set and we don't wrap the
|
2016-12-23 22:31:38 +03:00
|
|
|
// return type in a maybe.
|
|
|
|
function unsound_dict_has_every_key(
|
|
|
|
o: {[k:string]:X},
|
|
|
|
) {
|
|
|
|
(o.p: X); // ok
|
2017-04-12 23:41:51 +03:00
|
|
|
(o["p"]: X); // ok
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2017-02-22 00:59:19 +03:00
|
|
|
// This isn't specific behavior to dictionaries, but for completeness...
|
2016-12-23 22:31:38 +03:00
|
|
|
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},
|
|
|
|
) {
|
2017-04-12 23:41:51 +03:00
|
|
|
o.prop = "err"; // error: string ~> number
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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},
|
|
|
|
) {
|
2017-04-12 23:41:51 +03:00
|
|
|
o[0] = "ok";
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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**
|
2017-02-22 00:59:19 +03:00
|
|
|
// Because we support non-string props w/ bracket notation, it's possible to
|
2016-12-23 22:31:38 +03:00
|
|
|
// write into a declared prop unsoundly.
|
|
|
|
function unsound_string_conversion_alias_declared_prop(
|
2017-04-12 23:41:51 +03:00
|
|
|
o: {[k:number]:any, "0":X},
|
2016-12-23 22:31:38 +03:00
|
|
|
) {
|
2017-04-12 23:41:51 +03:00
|
|
|
o[0] = "not-x"; // a["0"] no longer X
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2017-02-22 00:59:19 +03:00
|
|
|
// argument that we shouldn't really care about this, since we ignore the fact
|
2016-12-23 22:31:38 +03:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2016-12-27 21:29:31 +03:00
|
|
|
/* 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.
|
|
|
|
*
|
2017-02-22 00:59:19 +03:00
|
|
|
* A dictionary is assumed to have every property described by it's key type.
|
2016-12-27 21:29:31 +03:00
|
|
|
* This behavior is similar to the behavior of arrays, which are assumed to have
|
|
|
|
* a value at every index.
|
|
|
|
*
|
|
|
|
* @flow
|
|
|
|
*/
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
// Some logic is variance-sensitive.
|
|
|
|
class A {}
|
|
|
|
class B extends A {}
|
|
|
|
class C extends B {}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// Just a couple of short type names. Compare to string/number.
|
2016-12-27 21:29:31 +03:00
|
|
|
class X {}
|
|
|
|
class Y {}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// Any property can be set on a dict with string keys.
|
2016-12-27 21:29:31 +03:00
|
|
|
function set_prop_to_string_key(o: { [k: string]: any }) {
|
2017-04-12 23:41:51 +03:00
|
|
|
o.prop = "ok";
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// **UNSOUND**
|
2017-02-22 00:59:19 +03:00
|
|
|
// This is allowed by design. We don't track get/set and we don't wrap the
|
2017-01-10 05:49:06 +03:00
|
|
|
// return type in a maybe.
|
2016-12-27 21:29:31 +03:00
|
|
|
function unsound_dict_has_every_key(o: { [k: string]: X }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
(o.p: X); // ok
|
2017-04-12 23:41:51 +03:00
|
|
|
(o["p"]: X); // ok
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// As with any object type, we can assign subtypes to properties.
|
2016-12-27 21:29:31 +03:00
|
|
|
function set_prop_covariant(o: { [k: string]: B }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
o.p = new A(); // error, A ~> B
|
|
|
|
o.p = new B(); // ok
|
2017-01-11 02:51:46 +03:00
|
|
|
o.p = new C(); // ok
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-02-22 00:59:19 +03:00
|
|
|
// This isn't specific behavior to dictionaries, but for completeness...
|
2016-12-27 21:29:31 +03:00
|
|
|
function get_prop_contravariant(o: { [k: string]: B }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
(o.p: A); // ok
|
|
|
|
(o.p: B); // ok
|
2017-01-10 05:49:06 +03:00
|
|
|
(o.p: C); // error, C ~> B
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// Dot-notation can not be used to add properties to dictionaries with
|
|
|
|
// non-string keys, because keys are strings.
|
2016-12-27 21:29:31 +03:00
|
|
|
function add_prop_to_nonstring_key_dot(o: { [k: number]: any }) {
|
2017-04-12 23:41:51 +03:00
|
|
|
o.prop = "err"; // error: string ~> number
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// 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.
|
2016-12-27 21:29:31 +03:00
|
|
|
function add_prop_to_nonstring_key_bracket(o: { [k: number]: any }) {
|
2017-04-12 23:41:51 +03:00
|
|
|
o[0] = "ok";
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// Objects can be part dict, part not by mixing an indexer with declared props.
|
2017-01-05 01:26:44 +03:00
|
|
|
function mix_with_declared_props(o: { [k: number]: X, p: Y }, x: X, y: Y) {
|
2017-01-26 22:57:43 +03:00
|
|
|
(o[0]: X); // ok
|
|
|
|
(o.p: Y); // ok
|
|
|
|
o[0] = x; // ok
|
2017-01-10 05:49:06 +03:00
|
|
|
o.p = y; // ok
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// Indeed, dict types are still Objects and have Object.prototype stuff
|
2017-05-02 05:12:53 +03:00
|
|
|
function object_prototype(o: {
|
|
|
|
[k: string]: number
|
|
|
|
}): { [k: string]: number, +toString: () => string } {
|
2017-01-26 22:57:43 +03:00
|
|
|
(o.toString(): boolean); // error: string ~> boolean
|
2017-01-10 05:49:06 +03:00
|
|
|
return o; // ok
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// **UNSOUND**
|
2017-02-22 00:59:19 +03:00
|
|
|
// Because we support non-string props w/ bracket notation, it's possible to
|
2017-01-10 05:49:06 +03:00
|
|
|
// write into a declared prop unsoundly.
|
2017-05-02 05:12:53 +03:00
|
|
|
function unsound_string_conversion_alias_declared_prop(o: {
|
|
|
|
[k: number]: any,
|
|
|
|
"0": X
|
|
|
|
}) {
|
2017-04-12 23:41:51 +03:00
|
|
|
o[0] = "not-x"; // a["0"] no longer X
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function unification_dict_values_invariant(x: Array<{ [k: string]: B }>) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: Array<{ [k: string]: A }> = x; // error
|
|
|
|
a[0].p = new A(); // in[0].p no longer B
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let b: Array<{ [k: string]: B }> = x; // ok
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let c: Array<{ [k: string]: C }> = x; // error
|
2017-01-10 05:49:06 +03:00
|
|
|
(x[0].p: C); // not true
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_dict_values_invariant(x: { [k: string]: B }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: { [k: string]: A } = x; // error
|
|
|
|
a.p = new A(); // x[0].p no longer B
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let b: { [k: string]: B } = x; // ok
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let c: { [k: string]: C } = x; // error
|
2017-01-10 05:49:06 +03:00
|
|
|
(x.p: C); // not true
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_dict_values_fresh_exception() {
|
2017-01-10 05:49:06 +03:00
|
|
|
let a: { [k: string]: A } = {
|
2017-01-26 22:57:43 +03:00
|
|
|
a: new A(), // ok, A == A
|
|
|
|
b: new B(), // ok, B <: A
|
|
|
|
c: new C() // ok, C <: A
|
2017-01-10 05:49:06 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
let b: { [k: string]: B } = {
|
2017-01-26 22:57:43 +03:00
|
|
|
a: new A(), // error, A not <: B
|
|
|
|
b: new B(), // ok, B == B
|
|
|
|
c: new C() // ok, C <: A
|
2017-01-10 05:49:06 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
let c: { [k: string]: C } = {
|
2017-01-26 22:57:43 +03:00
|
|
|
a: new A(), // error, A not <: C
|
|
|
|
b: new B(), // error, A not <: C
|
|
|
|
c: new C() // ok, C == C
|
2017-01-10 05:49:06 +03:00
|
|
|
};
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// Actually, unsound_string_conversion_alias_declared_prop behavior makes an
|
2017-02-22 00:59:19 +03:00
|
|
|
// argument that we shouldn't really care about this, since we ignore the fact
|
2017-01-10 05:49:06 +03:00
|
|
|
// 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.
|
2016-12-27 21:29:31 +03:00
|
|
|
function unification_dict_keys_invariant(x: Array<{ [k: B]: any }>) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: Array<{ [k: A]: any }> = x; // error
|
|
|
|
let b: Array<{ [k: B]: any }> = x; // ok
|
2017-01-10 05:49:06 +03:00
|
|
|
let c: Array<{ [k: C]: any }> = x; // error
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_dict_keys_invariant(x: { [k: B]: any }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: { [k: A]: any } = x; // error
|
|
|
|
let b: { [k: B]: any } = x; // ok
|
2017-01-10 05:49:06 +03:00
|
|
|
let c: { [k: C]: any } = x; // error
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function unification_mix_with_declared_props_invariant_l(
|
|
|
|
x: Array<{ [k: string]: B }>
|
|
|
|
) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: Array<{ [k: string]: B, p: A }> = x; // error: A ~> B
|
|
|
|
a[0].p = new A(); // x[0].p no longer B
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let b: Array<{ [k: string]: B, p: B }> = x; // ok
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let c: Array<{ [k: string]: B, p: C }> = x; // error
|
2017-01-10 05:49:06 +03:00
|
|
|
(x[0].p: C); // not true
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function unification_mix_with_declared_props_invariant_r(
|
2017-01-05 01:26:44 +03:00
|
|
|
xa: Array<{ [k: string]: A, p: B }>,
|
|
|
|
xb: Array<{ [k: string]: B, p: B }>,
|
|
|
|
xc: Array<{ [k: string]: C, p: B }>
|
2016-12-27 21:29:31 +03:00
|
|
|
) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: Array<{ [k: string]: A }> = xa; // error
|
|
|
|
a[0].p = new A(); // xa[0].p no longer B
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let b: Array<{ [k: string]: B }> = xb; // ok
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let c: Array<{ [k: string]: C }> = xc; // error
|
2017-01-10 05:49:06 +03:00
|
|
|
(xc[0].p: C); // not true
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_mix_with_declared_props_invariant_l(x: { [k: string]: B }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: { [k: string]: B, p: A } = x; // error: A ~> B
|
|
|
|
a.p = new A(); // x.p no longer B
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let b: { [k: string]: B, p: B } = x; // ok
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let c: { [k: string]: B, p: C } = x; // error
|
2017-01-10 05:49:06 +03:00
|
|
|
(x.p: C); // not true
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_mix_with_declared_props_invariant_r(
|
2017-01-05 01:26:44 +03:00
|
|
|
xa: { [k: string]: A, p: B },
|
|
|
|
xb: { [k: string]: B, p: B },
|
|
|
|
xc: { [k: string]: C, p: B }
|
2016-12-27 21:29:31 +03:00
|
|
|
) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: { [k: string]: A } = xa; // error
|
|
|
|
a.p = new A(); // xa.p no longer B
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let b: { [k: string]: B } = xb; // ok
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let c: { [k: string]: C } = xc; // error
|
2017-01-10 05:49:06 +03:00
|
|
|
(xc.p: C); // not true
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-01-02 04:33:53 +03:00
|
|
|
function unification_dict_to_obj(
|
|
|
|
x: Array<{ [k: string]: X }>
|
|
|
|
): Array<{ p: X }> {
|
2017-01-10 05:49:06 +03:00
|
|
|
return x; // error: if allowed, could write {p:X,q:Y} into \`x\`
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-01-02 04:33:53 +03:00
|
|
|
function unification_obj_to_dict(
|
|
|
|
x: Array<{ p: X }>
|
|
|
|
): Array<{ [k: string]: X }> {
|
2017-01-10 05:49:06 +03:00
|
|
|
return x; // error: if allowed, could write {p:X,q:Y} into returned array
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_dict_to_obj(x: { [k: string]: B }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: { p: A } = x; // error
|
|
|
|
a.p = new A(); // x.p no longer B
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let b: { p: B } = x; // ok
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let c: { p: C } = x; // error
|
2017-01-10 05:49:06 +03:00
|
|
|
(x.p: C); // not true
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_obj_to_dict(x: { p: B }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: { [k: string]: A } = x; // error
|
|
|
|
a.p = new A(); // x.p no longer B
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
let b: { [k: string]: B } = x;
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
let c: { [k: string]: C } = x; // error
|
2017-01-10 05:49:06 +03:00
|
|
|
(x.p: C); // not true
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// Only props in l which are not in u must match indexer, but must do so
|
|
|
|
// exactly.
|
2017-01-05 01:26:44 +03:00
|
|
|
function subtype_obj_to_mixed(x: { p: B, x: X }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: { [k: string]: A, x: X } = x; // error (as above), but exclusive of x
|
|
|
|
let b: { [k: string]: B, x: X } = x; // ok,
|
2017-01-10 05:49:06 +03:00
|
|
|
let c: { [k: string]: C, x: X } = x; // error (as above), but exclusive of x
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function unification_dict_to_mixed(x: Array<{ [k: string]: B }>) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: Array<{ [k: string]: B, p: A }> = x; // error
|
|
|
|
let b: Array<{ [k: string]: B, p: B }> = x; // ok
|
2017-01-10 05:49:06 +03:00
|
|
|
let c: Array<{ [k: string]: B, p: C }> = x; // error
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_dict_to_mixed(x: { [k: string]: B }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
let a: { [k: string]: B, p: A } = x; // error
|
|
|
|
let b: { [k: string]: B, p: B } = x; // ok
|
2017-01-10 05:49:06 +03:00
|
|
|
let c: { [k: string]: B, p: C } = x; // error
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_dict_to_optional_a(x: { [k: string]: B }) {
|
2017-01-10 05:49:06 +03:00
|
|
|
let a: { p?: A } = x; // error
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_dict_to_optional_b(x: { [k: string]: B }) {
|
2017-01-10 05:49:06 +03:00
|
|
|
let b: { p?: B } = x; // ok
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function subtype_dict_to_optional_c(x: { [k: string]: B }) {
|
2017-01-10 05:49:06 +03:00
|
|
|
let c: { p?: C } = x; // error
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-02-04 00:56:06 +03:00
|
|
|
function subtype_optional_a_to_dict(x: { p?: A }): { [k: string]: B } {
|
|
|
|
// error: A ~> B
|
2016-12-27 21:29:31 +03:00
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-02-04 00:56:06 +03:00
|
|
|
function subtype_optional_b_to_dict(x: { p?: B }): { [k: string]: B } {
|
|
|
|
// ok
|
2016-12-27 21:29:31 +03:00
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-02-04 00:56:06 +03:00
|
|
|
function subtype_optional_c_to_dict(x: { p?: C }): { [k: string]: B } {
|
|
|
|
// error: C ~> B
|
2016-12-27 21:29:31 +03:00
|
|
|
return x;
|
2017-01-11 18:16:38 +03:00
|
|
|
}
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
`;
|
|
|
|
|
2017-03-22 03:38:28 +03:00
|
|
|
exports[`incompatible.js 1`] = `
|
2017-04-12 23:41:51 +03:00
|
|
|
/* @flow */
|
2016-12-23 22:31:38 +03:00
|
|
|
|
|
|
|
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)
|
2017-02-22 00:59:19 +03:00
|
|
|
var c : {[key: string]: ?string} = b; // 2 errors, since c['x'] = null updates b
|
2016-12-23 22:31:38 +03:00
|
|
|
|
|
|
|
// 2 errors (number !~> string, string !~> number)
|
|
|
|
function foo0(x: Array<{[key: string]: number}>): Array<{[key: string]: string}> {
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
2017-02-22 00:59:19 +03:00
|
|
|
// error, fooBar:string !~> number (x's dictionary)
|
2016-12-23 22:31:38 +03:00
|
|
|
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}> {
|
2017-02-22 00:59:19 +03:00
|
|
|
x[0].fooBar = 123; // OK, since number ~> mixed (x elem's dictionary)
|
2016-12-23 22:31:38 +03:00
|
|
|
return x; // error: mixed ~> string
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK, since we assume dictionaries have every key
|
|
|
|
function foo3(x: {[key: string]: number}): {foo: number} {
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
2017-02-22 00:59:19 +03:00
|
|
|
// error: foo can't exist in x
|
2016-12-23 22:31:38 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2016-12-27 21:29:31 +03:00
|
|
|
/* @flow */
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
var x: { [key: string]: string } = {};
|
2017-01-26 22:57:43 +03:00
|
|
|
var y: { [key: string]: number } = x; // 2 errors, number !~> string & vice versa
|
|
|
|
var z: { [key: number]: string } = x; // 2 errors, string !~> number & vice versa
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
var a: { [key: string]: ?string } = {};
|
2017-01-26 22:57:43 +03:00
|
|
|
var b: { [key: string]: string } = a; // 2 errors (null & undefined)
|
2017-02-22 00:59:19 +03:00
|
|
|
var c: { [key: string]: ?string } = b; // 2 errors, since c['x'] = null updates b
|
2017-01-31 19:27:44 +03:00
|
|
|
|
2017-01-10 05:49:06 +03:00
|
|
|
// 2 errors (number !~> string, string !~> number)
|
2017-01-02 04:33:53 +03:00
|
|
|
function foo0(
|
|
|
|
x: Array<{ [key: string]: number }>
|
|
|
|
): Array<{ [key: string]: string }> {
|
2016-12-27 21:29:31 +03:00
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-02-22 00:59:19 +03:00
|
|
|
// error, fooBar:string !~> number (x's dictionary)
|
2017-01-02 04:33:53 +03:00
|
|
|
function foo1(
|
|
|
|
x: Array<{ [key: string]: number }>
|
2017-01-05 01:26:44 +03:00
|
|
|
): Array<{ [key: string]: number, fooBar: string }> {
|
2016-12-27 21:29:31 +03:00
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-01-02 04:33:53 +03:00
|
|
|
function foo2(
|
|
|
|
x: Array<{ [key: string]: mixed }>
|
2017-01-05 01:26:44 +03:00
|
|
|
): Array<{ [key: string]: mixed, fooBar: string }> {
|
2017-02-22 00:59:19 +03:00
|
|
|
x[0].fooBar = 123; // OK, since number ~> mixed (x elem's dictionary)
|
2017-01-10 05:49:06 +03:00
|
|
|
return x; // error: mixed ~> string
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// OK, since we assume dictionaries have every key
|
2016-12-27 21:29:31 +03:00
|
|
|
function foo3(x: { [key: string]: number }): { foo: number } {
|
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-02-22 00:59:19 +03:00
|
|
|
// error: foo can't exist in x
|
2017-05-02 05:12:53 +03:00
|
|
|
function foo4(x: {
|
|
|
|
[key: string]: number
|
|
|
|
}): { [key: string]: number, foo: string } {
|
2016-12-27 21:29:31 +03:00
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// error, some prop in x could be incompatible (covariance)
|
2016-12-27 21:29:31 +03:00
|
|
|
function foo5(x: Array<{ [key: string]: number }>): Array<{ foo: number }> {
|
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
|
|
|
// error, some prop in return could be incompatible
|
2016-12-27 21:29:31 +03:00
|
|
|
function foo6(x: Array<{ foo: number }>): Array<{ [key: string]: number }> {
|
|
|
|
return x;
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-05-02 00:42:52 +03:00
|
|
|
function foo7(x: { bar: string, [key: string]: number }) {
|
2016-12-27 21:29:31 +03:00
|
|
|
(x.bar: string);
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
function foo8(x: { [key: string]: number }) {
|
2017-01-26 22:57:43 +03:00
|
|
|
(x.foo: string); // error
|
2016-12-27 21:29:31 +03:00
|
|
|
(x.foo: number);
|
2017-01-11 18:16:38 +03:00
|
|
|
}
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
`;
|
|
|
|
|
2017-03-22 03:38:28 +03:00
|
|
|
exports[`issue-1745.js 1`] = `
|
2017-04-12 23:41:51 +03:00
|
|
|
/* @flow */
|
2016-12-23 22:31:38 +03:00
|
|
|
|
|
|
|
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 */
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
class A {
|
|
|
|
x: { [k: string]: number };
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
m1() {
|
2017-01-10 05:49:06 +03:00
|
|
|
this.x = { bar: 0 }; // no error
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
m2() {
|
2017-01-10 05:49:06 +03:00
|
|
|
this.x.foo = 0; // no error
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
class B {
|
|
|
|
x: { [k: string]: number };
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
m2() {
|
2017-01-10 05:49:06 +03:00
|
|
|
this.x.foo = 0; // no error
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
m1() {
|
2017-01-10 05:49:06 +03:00
|
|
|
this.x = { bar: 0 }; // no error
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
2017-01-11 18:16:38 +03:00
|
|
|
}
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
`;
|
|
|
|
|
2017-03-22 03:38:28 +03:00
|
|
|
exports[`test.js 1`] = `
|
2017-04-12 23:41:51 +03:00
|
|
|
type Params = {count: number; [name: string]: string};
|
2016-12-23 22:31:38 +03:00
|
|
|
type QueryFunction = (params: Params) => string;
|
|
|
|
|
|
|
|
var o: { foo: QueryFunction } = {
|
|
|
|
foo(params) {
|
|
|
|
return params.count; // error, number ~/~ string
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = o;
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2017-05-02 00:42:52 +03:00
|
|
|
type Params = { count: number, [name: string]: string };
|
2016-12-30 08:01:44 +03:00
|
|
|
type QueryFunction = (params: Params) => string;
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-27 21:29:31 +03:00
|
|
|
var o: { foo: QueryFunction } = {
|
|
|
|
foo(params) {
|
2017-01-10 05:49:06 +03:00
|
|
|
return params.count; // error, number ~/~ string
|
2016-12-27 21:29:31 +03:00
|
|
|
}
|
|
|
|
};
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2017-01-11 18:16:38 +03:00
|
|
|
module.exports = o;
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
`;
|
|
|
|
|
2017-03-22 03:38:28 +03:00
|
|
|
exports[`test_client.js 1`] = `
|
2017-04-12 23:41:51 +03:00
|
|
|
var o = require('./test');
|
2016-12-23 22:31:38 +03:00
|
|
|
|
|
|
|
o.foo = function (params) {
|
|
|
|
return params.count; // error, number ~/~ string
|
|
|
|
}
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2017-04-12 23:41:51 +03:00
|
|
|
var o = require("./test");
|
2017-01-10 05:49:06 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
o.foo = function(params) {
|
2017-01-10 05:49:06 +03:00
|
|
|
return params.count; // error, number ~/~ string
|
2017-01-11 18:16:38 +03:00
|
|
|
};
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
`;
|