typescript - Polymorf this-nyckel fungerar inte som förväntat inne i metodkroppen


Den här koden fungerar:

class Foo {
  axisControl: string = "string";
  directionControl: string = "string";

  filteredAxis: string[] = ["string"];
  filteredDirection: string[] = ["string"];

  test(filteredName: `filtered${string}` & keyof Foo) {
    this[filteredName] = ["otherString"];
  }
}

this[filteredName] löses framgångsrikt till typen string[]. Dock innebär det att klassen refererar till sig själv med sitt namn. Jag vill använda det polymorfa this istället:

class Foo {
  axisControl: string = "string";
  directionControl: string = "string";

  filteredAxis: string[] = ["string"];
  filteredDirection: string[] = ["string"];

  test(filteredName: `filtered${string}` & keyof this) { // polymorphic this
    this[filteredName] = ["otherString"]; // Type 'string[]' is not assignable to type 'this[`filtered${string}` & keyof this]'.
  }
}

Men av någon anledning fungerar det inte och den uppklarade typen är this[`filtered${string}` & keyof this] vilket inte tillåter mig att göra något.

Svar #1

Beroende på om metoden anropas på en instans av klassen eller en instans av en utökande klass, kan den slutliga typen som baseras på this variera, därför löses den inte i funktionskroppen eftersom den ännu inte vet utförandekontexten.

Följande kod har också en möjlighet att existera och är helt giltig:

const test = Foo.prototype.test;

test("filteredAxis");

I så fall bör this lösas till undefined eller window (beroende på strikt/slafsigt läge). Det finns andra fall där this kan vara ett objekt bundet med Function.prototype.bind, Function.prototype.apply eller Function.prototype.call.

Men kompilatorn kastar inget fel som förväntat. Men det finns en möjlighet att välja att upprätthålla detta genom att uttryckligen annotera metoden så här:

test(this: this, filteredName: `filtered${string}` & keyof Foo)

Det pågår en diskussion här om en flagga som skulle möjliggöra detta beteende som standard: https://github.com/microsoft/TypeScript/issues/7968

Men även med detta beteende aktiverat, löses inte typen inuti funktionskroppen på grund av de möjliga utökande klasserna.

Sammanfattning

Inuti metodkroppen vet inte kompilatorn vilken möjlig utökande klass metoden anropas från, så typer baserade på this löses inte. De gör det först när kompilatorn vet vilken den verkliga klassen är.

class Foo {
  axisControl: string = "string";
  directionControl: string = "string";

  filteredAxis: string[] = ["string"];
  filteredDirection: string[] = ["string"];

  test(filteredName: `filtered${string}` & keyof this) { // polymorphic this
    return this[filteredName]; // Type does not resolve here (unknown current class)
  }
}

const foo = new Foo;
const result = foo.test("filteredAxis"); // But it does here: string[]

Som en slutsats tillåter this att ange typen för gränssnittet (parametrar och returvärde) men inte implementeringen av metoden.