/* @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]: unknown}>,
): Array<{[key: string]: unknown, 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 as string;
}

function foo8(x: {[key: string]: number}) {
  x.foo as string; // error
  x.foo as number;
}
