Skip to content

Inherited typing for class property initializers #10570

Description

Problem

Initializing a class member with things like { }, null, undefined, or [] has unexpected behavior.

class Base {
  favorites = ["red", "blue"];
}
class Derived extends Base {
  favorites = [];
  constructor() {
    this.favorites.push('green'); // Can't push string onto never[], wat?
  }
}
interface Settings {
  size?: number;
  color?: string;
}
class Base {
  settings: Settings = { size: 42 };
}
class Derived extends Base {
  settings = { };
  constructor() {
    if (big) this.settings = { siz: 100 }; // no error, wat?
  }
}

Solution

New rule: When a class property is initialized with exactly null, undefined, { }, or [], the type of the property is taken from the same property of the inherited type (if one exists), rather than the type of the initializer.

The inherited type is B & I1 & I2 & ... where B is the base class and I1, I2, ... are the implemented interfaces of the class.

Examples

interface Positionable {
  position: string | null;
}
class MyPos implements Positionable {
  position = null;
  setPos(x: string) {
    this.position = x;
  }
  getPos() {
    return this.position.subtr(3); // error detected
  }
}
class Base {
  items = ['one'];
}
class Derived extends Base {
  items = []; // no longer an implicit any
}
var x = new Derived();
x.items.push(10); // Error as expected

Bad Ideas We Thought Were good

image

Contextual typing plays poorly with other behavior such as unit type positions. Consider

enum E { A, B, C }
class Base {
  thing = E.A;
}
class Derived extends Base {
  thing = E.B;
  change() {
    this.thing = E.C; // Error! wat
  }
}

This turns into a big problem because the E.B expression is contextually typed by the unit-like type E.A | E.B | E.C and so acquires the specific type E.B rather than the intended type E! Daniel found this break in Azure.

/cc conspirators Daniel Rosenwasser (@DanielRosenwasser) Nathan Shively-Sanders (@sandersn)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions