microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.73k stars 12.45k forks source link

Type inference when combining inheritance, optional properties and union type #28763

Open adzhiljano opened 5 years ago

adzhiljano commented 5 years ago

TypeScript Version: 3.3.0-dev.20181130

type inference type inference inheritance type inference inheritance optional type inference inheritance optional property type inference inheritance optional union type type inference inheritance optional property union type

Code

export class ModelBase {
  modelBaseProp: boolean;

  constructor() {
  }
}

export class Model1 extends ModelBase {
  model1Prop?: number;

  constructor() {
    super();
  }
}

export class Model2 extends ModelBase {
  model2Prop?: string;

  constructor() {
    super();
  }
}

export class Test {
  test: Model1 | Model2;
  constructor() {
    this.test = new Model1();
    this.test.model1Prop = 1;

    this.test = new Model2();
    this.test.model2Prop = 'test';
  }
}

Expected behavior: No compilation errors.

Actual behavior: error TS2339: Property 'model1Prop' does not exist on type 'Model1 | Model2'. Property 'model1Prop' does not exist on type 'Model2' and error TS2339: Property 'model2Prop' does not exist on type 'Model1 | Model2'. Property 'model2Prop' does not exist on type 'Model1'.

Playground Link: [link](http://www.typescriptlang.org/play/#src=export%20class%20ModelBase%20%7B%0D%0A%20%20modelBaseProp%3F%3A%20boolean%3B%0D%0A%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Aexport%20class%20Model1%20extends%20ModelBase%20%7B%0D%0A%20%20model1Prop%3F%3A%20number%3B%0D%0A%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%20%20super()%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Aexport%20class%20Model2%20extends%20ModelBase%20%7B%0D%0A%20%20model2Prop%3F%3A%20string%3B%0D%0A%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%20%20super()%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Aexport%20class%20Test%20%7B%0D%0A%20%20test%3A%20Model1%20%7C%20Model2%3B%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%20%20this.test%20%3D%20new%20Model1()%3B%0D%0A%20%20%20%20this.test.model1Prop%20%3D%201%3B%0D%0A%0D%0A%20%20%20%20this.test%20%3D%20new%20Model2()%3B%0D%0A%20%20%20%20this.test.model2Prop%20%3D%20'test'%3B%0D%0A%20%20%7D%0D%0A%7D)

Additional info Making the properties in the derived classes non-optional works as expected. Note that the following is working properly: [Playground link](http://www.typescriptlang.org/play/#src=export%20class%20Model1%20%7B%0D%0A%20%20model1Prop%3F%3A%20number%3B%0D%0A%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Aexport%20class%20Model2%20%7B%0D%0A%20%20model2Prop%3F%3A%20string%3B%0D%0A%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Aexport%20class%20Test%20%7B%0D%0A%20%20test%3A%20Model1%20%7C%20Model2%3B%0D%0A%20%20constructor()%20%7B%0D%0A%20%20%20%20this.test%20%3D%20new%20Model1()%3B%0D%0A%20%20%20%20this.test.model1Prop%20%3D%201%3B%0D%0A%0D%0A%20%20%20%20this.test%20%3D%20new%20Model2()%3B%0D%0A%20%20%20%20this.test.model2Prop%20%3D%20'test'%3B%0D%0A%20%20%7D%0D%0A%7D)

export class Model1 {
  model1Prop?: number;

  constructor() {
  }
}

export class Model2 {
  model2Prop?: string;

  constructor() {
  }
}

export class Test {
  test: Model1 | Model2;
  constructor() {
    this.test = new Model1();
    this.test.model1Prop = 1;

    this.test = new Model2();
    this.test.model2Prop = 'test';
  }
}
RyanCavanaugh commented 5 years ago

Predates 2.0