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

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 @DanielRosenwasser @sandersn
Problem
Initializing a class member with things like
{ },null,undefined, or[]has unexpected behavior.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 & ...whereBis the base class andI1,I2,...are theimplemented interfaces of the class.Examples
Bad Ideas We Thought Were good
Contextual typing plays poorly with other behavior such as unit type positions. Consider
This turns into a big problem because the
E.Bexpression is contextually typed by the unit-like typeE.A | E.B | E.Cand so acquires the specific typeE.Brather than the intended typeE! Daniel found this break in Azure./cc conspirators @DanielRosenwasser @sandersn