Repository: TypeScript
Issue: Visual Studio 2015 provides inconsistent type info per request order
Labels: Bug

**TypeScript Version:** 

1.8.30 with VS2015

@ahejlsberg @vladima @mhegazy

![animation](https://cloud.githubusercontent.com/assets/3143368/15557972/834a6056-2311-11e6-8b61-b57841f67e52.gif)

``` ts
// monad.ts
export abstract class Monad<T> {
  private MONAD: T;
  constructor(protected thunk?: () => Monad<T>) {
  }
  public abstract bind<U>(f: (val: T) => Monad<U>): Monad<U>;
  public extract(): T
  public extract<U>(transform: (val: any) => U): T | U
  public extract<U>(transform?: (val: any) => U): T | U {
    return this.evaluate().extract(transform);
  }
  protected memory_: this;
  protected evaluate(): this {
    return this.memory_ = this.memory_ || <this>this.thunk();
  }
  public assert<S extends Monad<T>>(type?: S): Monad<T> {
    return this;
  }
}
// maybe.impl.ts
import {Monad} from './monad';

export class Maybe<T> extends Monad<T> {
  protected MAYBE: Just<T> | Nothing;
  constructor(protected thunk?: () => Maybe<T>) {
    super(thunk);
  }
  public bind<U>(f: (val: T) => Just<U> | Nothing): Maybe<U>
  public bind<U>(f: (val: T) => Maybe<U>): Maybe<U>
  public bind<U>(f: (val: T) => Maybe<U>): Maybe<U> {
    return new Maybe<U>(() => {
      const m: Maybe<T> = this.evaluate();
      if (m instanceof Just) {
        return f(m.extract());
      }
      if (m instanceof Nothing) {
        return m;
      }
      if (m instanceof Maybe) {
        return (<Maybe<T>>m).bind(f);
      }
      throw new TypeError(`ArchStream: Maybe: Invalid monad value.\n\t${m}`);
    });
  }
  public extract(): T
  public extract<U>(transform: () => U): T | U
  public extract<U>(transform?: () => U): T | U {
    return super.extract(transform);
  }
  public assert<S extends Maybe<T>>(type?: S): Maybe<T> {
    return this;
  }
}

export class Just<T> extends Maybe<T> {
  protected MAYBE: Just<T>;
  constructor(private val_: T) {
    super();
  }
  public bind<U>(f: (val: T) => Just<U> | Nothing): Maybe<U>
  public bind<U>(f: (val: T) => Maybe<U>): Maybe<U>
  public bind<U>(f: (val: T) => Maybe<U>): Maybe<U> {
    return new Maybe(() => this).bind(f);
  }
  public extract<U>(transform?: () => U): T {
    return this.val_;
  }
  public assert<S extends Just<T>>(type?: S): Just<T> {
    return this;
  }
}

export class Nothing extends Maybe<any> {
  protected MAYBE: Nothing;
  public bind(f: (val: any) => Maybe<any>): Nothing {
    return this;
  }
  public extract(): any
  public extract<U>(transform: () => U): U
  public extract<U>(transform?: () => U): U {
    if (!transform) throw void 0;
    return transform();
  }
  public assert<S extends Nothing>(type?: S): Nothing {
    return this;
  }
}
// maybe.ts
import {Maybe as Maybe_, Just as Just_, Nothing as Nothing_} from './maybe.impl';

export namespace Maybe {
  export type Just<T> = Just_<T>;
  export function Just<T>(val: T): Just<T> {
    return new Just_(val);
  }
  export type Nothing = Nothing_;
  export const Nothing = new Nothing_();
  export const Return = Just;
}

export type Maybe<T> = Just<T> | Nothing | Maybe_<T>;
export type Just<T> = Maybe.Just<T>;
export const Just = Maybe.Just;
export type Nothing = Maybe.Nothing;
export const Nothing = Maybe.Nothing;
export const Return = Just;
// maybe.test.ts

    it('Maybe', () => {
      const result = Return(0)
        .bind(n => Just(n) || Nothing || Just(n).bind(n => Just(n) || Nothing))
        .bind(n => Just(n) || Nothing || Just(n).bind(n => Just(n) || Nothing))
        .extract(() => 'Nothing');
      assert(result === 0);
    });
```