/* @flow */

type Type = Name | ListType | NonNullType;
type Name = {kind: 'Name', value: string, type: void, ...};
type ListType = {kind: 'ListType', type: Type, ...};
type NonNullType = {kind: 'NonNullType', type: Name | ListType | BadType, ...};
type BadType = {...};

function getTypeASTName(typeAST: Type): string {
  if (!typeAST.type) throw new Error('Must be wrapping type'); // OK
  return getTypeASTName(typeAST.type); // error, BadType not a subtype of Type
}

let tests = [
  function (x: {done: true, result: string, ...} | {done: false, ...}) {
    if (x.done) {
      return x.result;
    }
    return x.result; // error
  },

  function (x: {done: true, result: string, ...} | {foo: string, ...}) {
    if (x.done) {
      return x.result; // error, consider { foo: "herp", done: "derp" }
    }
    return x.result; // error
  },

  function () {
    type T = {foo: {...}, bar: string, ...} | {baz: string, quux: string, ...};

    function testAlwaysTruthyProp(t: T) {
      if (t.foo) {
        t.bar as string; // error, consider { baz: "x", quux: "y", foo: "boom" }
      } else {
        t.quux as string; // ok. since foo is an object (always truthy), the
        // else case completely rules out the first branch of
        // the union.
      }
    }

    function testSometimesTruthyProp(t: T) {
      if (t.bar) {
        t.foo as interface {}; // error, consider { baz: "x", quux: "y", bar: "boom" }
      } else {
        t.quux as string; // error, consider { foo: {}, bar: "" }
      }
    }
  },

  function (o: null | {...}) {
    if (o.p) {
    } // 2 errors: property `p` not found on null and not found in {}
  },
];
