question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Inherited typing for class property initializers

See original GitHub issue

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 @DanielRosenwasser @sandersn

Issue Analytics

  • State:open
  • Created 7 years ago
  • Reactions:41
  • Comments:22 (12 by maintainers)

github_iconTop GitHub Comments

8reactions
paddotkcommented, Aug 17, 2022

This issue has been open since 2016, any updates?

8reactions
Veetahacommented, Apr 27, 2019

Is there any update on this issue? I am eager to see it in the closest releases.

Regarding this feedback:

Of course, there may be some breaking changes, but those will appear in bad code (with implicit any) and some of them can be fixed, like setting the type of the property by its initializer if it is assignable to the type from base/implemented interface. I think that this feature will bring more advantages (like less boilerplate and narrowing the sources of truth throughout the types of TypeScript apps).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Documentation - Classes - TypeScript
How classes work in TypeScript. ... Just like with const , let , and var , the initializer of a class property will...
Read more >
Initialization — The Swift Programming Language (Swift 5.7)
All of a class's stored properties—including any properties the class inherits from its superclass—must be assigned an initial value during initialization.
Read more >
Typescript inherited property initialized in a function is ...
A little bit of background, I'm migrating my source code from Create React App to Next.JS and TypeScript is compiled differently. Look at...
Read more >
Understanding Class Inheritance in Python 3 | DigitalOcean
Inheritance is when a class uses code constructed within another class. If we think of inheritance in terms of biology, we can think...
Read more >
Object Initializers: Named and Anonymous Types - Visual Basic
Object initializers provide a simple way to call the constructor of a type and then set the values of some or all properties...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found