Kodokon kodokon.com

Überladungen, typisiertes this und Varianz

Verstehe die Auflösung von Überladungen, den Pseudoparameter this und die Varianzregeln, die die Zuweisbarkeit von Funktionen bestimmen.

10 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Eine überladene Funktion stellt eine Liste öffentlicher Signaturen bereit, gefolgt von einer einzigen Implementierungssignatur. Ein wesentlicher Punkt der Spezifikation: Letztere ist von außen unsichtbar; aufrufbar sind nur die deklarierten Überladungen. Die Auflösung geht die Signaturen von oben nach unten durch und behält die erste passende: Stelle die spezifischste Signatur immer nach vorn, sonst wird sie nie ausgewählt.

TYPESCRIPT
function parse(input: string): number;
function parse(input: string[]): number[];
function parse(input: string | string[]) {
  return Array.isArray(input)
    ? input.map((s) => s.length)
    : input.length;
}

const single = parse("abc");      // number
const many = parse(["a", "bb"]);  // number[]
Jeder Aufruf löst sich zu einer genauen Überladung auf.

TypeScript erlaubt dir, an erster Stelle einen Pseudoparameter this zu deklarieren. Er wird im erzeugten JavaScript vollständig gelöscht: Es handelt sich um eine rein statische Einschränkung des Aufrufkontexts, die bei aktiviertem noImplicitThis geprüft wird. Mit den Hilfstypen ThisParameterType und OmitThisParameter kannst du ihn herausziehen oder entfernen, was für das Typisieren von Funktionen unerlässlich ist, die mit call, apply oder bind verwendet werden sollen.

TYPESCRIPT
function greet(this: { name: string }, msg: string) {
  return `${msg}, ${this.name}`;
}

type Ctx = ThisParameterType<typeof greet>;
// { name: string }
type Fn = OmitThisParameter<typeof greet>;
// (msg: string) => string

const user = { name: "Ada", greet };
user.greet("Hello"); // "Hello, Ada"

// greet("Hi"); // error: missing this context
Der Parameter this schränkt die Aufrufstelle ein, nicht das erzeugte JS.

Varianz beschreibt, wie sich die Zuweisbarkeit durch einen Typkonstruktor fortpflanzt. Bei Funktionen sind Rückgabetypen kovariant und die Parameter mit strictFunctionTypes kontravariant: Eine Funktion, die einen weiteren Typ akzeptiert, kann eine Funktion ersetzen, die einen engeren Typ akzeptiert, niemals umgekehrt. Eine bewusste Ausnahme: Die Parameter von Methoden, die in der Kurzschreibweise deklariert sind, bleiben bivariant, ein historischer Kompromiss, ohne den Array<Dog> nicht an Array<Animal> zuweisbar wäre. Seit 4.7 kannst du die erwartete Varianz mit in und out annotieren, zum Beispiel interface Producer<out T>, und der Compiler überprüft sie.

TYPESCRIPT
interface Animal { name: string }
interface Dog extends Animal { bark(): void }

declare let f: (a: Dog) => void;
declare let g: (a: Animal) => void;

f = g; // OK: g accepts a wider type, that is safe
// g = f; // error with strictFunctionTypes

interface Sink { accept(a: Animal): void }
interface DogSink { accept(a: Dog): void }
declare let s: Sink;
declare let d: DogSink;

d = s; s = d; // both compile: bivariance!
Strikte Funktions-Eigenschaften, bivariante Methoden.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Welche Signatur wählt der Compiler beim Aufruf einer überladenen Funktion?
    • Die allgemeinste Signatur
    • Die erste passende Signatur in der Reihenfolge der Deklaration
    • Die Implementierungssignatur
    • Die Union aller Signaturen
  2. Was passiert mit dem Parameter this einer Funktion im erzeugten JavaScript?
    • Er wird zu einem echten ersten Argument
    • Er verschwindet: Es ist eine rein statische Annotation
    • Er wird durch einen Aufruf von bind ersetzt
    • Er verursacht einen Laufzeitfehler, wenn er fehlt
  3. Welche Schreibweise profitiert mit strictFunctionTypes von der strikten kontravarianten Prüfung der Parameter?
    • Methoden in der Kurzschreibweise, etwa m(a: X): void
    • Eigenschaften mit Funktionstyp, etwa m: (a: X) => void
    • Beide Schreibweisen
    • Keine von beiden