microsoft / microsoft/TypeScript
Inherited typing for class property initializers
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
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

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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The issue names no files, tests, or entry points. Start by locating TypeScript's class-property type inference and type-checking logic, then use the Base/Derived and implements examples as regression cases. Done means null, undefined, {}, and [] inherit the matching property type without introducing the enum contextual-typing problem described here.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 38/100