microsoft / microsoft/TypeScript

Inherited typing for class property initializers

Offen
#10,570 27 Kommentare 54 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

In Discussion Suggestion
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.4k
Ø Merge
1 T. 19 Std.
Gemergte PRs (30 T.)
117

Beschreibung

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

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Das Issue nennt keine Dateien, Tests oder Einstiegspunkte. Beginne damit, die Logik zur Typinferenz und Typprüfung von Klasseneigenschaften in TypeScript zu lokalisieren, und verwende dann die Beispiele zu Base/Derived und implements als Regressionstests. Als erledigt gilt die Aufgabe, wenn null, undefined, {} und [] den passenden Eigenschaftstyp erben, ohne das hier beschriebene Problem der kontextuellen Typisierung von enum einzuführen.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
typescript
Bereich
compilers
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Ruhig
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
38/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.