javascript - Vad gör OmitThisParameter från ES5-definitioner i TypeScript?


Jag hittade denna typ i ES5-definitionerna för TypeScript, och undrade vad den gör eftersom den enkla beskrivningen är lite vag.

/**
 * Removes the 'this' parameter from a function type.
 */
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;

källkod

Beskrivningen antyder att this är en typ, men när man tittar på koden är det inte riktigt klart vad de menar med det.

Jag har sökt i Microsofts dokumentation för TypeScript, men det verkar inte finnas några referenser till hur detta används eller varför.

Här är ett exempel på källkod där jag har sett det användas.

export class Example {
   funcRef: OmitThisParameter<()=> void>;
   func() { }
   constructor() { this.funcRef = this.func.bind(this); }
}

Det verkar för mig som att det inte finns några fördelar med statisk typ här. Användningen av funcRef verkar inte ha några restriktioner. Kan det vara så att detta förhindrar att funcRef kan användas som en konstruktörsfunktion?

Om jag gör följande ändringar kompileras källkoden utan några problem.

export class Example {
   funcRef: () => void;
   func() { }
   constructor() { this.funcRef = this.func.bind(this); }
}

Är inte den här typen identisk med en pilfunktion, men vi deklarerar inte typer för pilfunktioner.

Så jag letar efter ett svar som förklarar vilka typfördelar detta har, och varför vi bör använda denna typ, eftersom jag mycket sällan ser den använd.

Kan du ge ett exempel på källkod som skulle få TypeScript att ge en typtypvarning?

Svar #1

En this-parameter är en pseudo-parameter som du placerar först i en funktionssignatur, och den anger att inuti funktionen kommer this-kontexten att ha den definierade typen. Den gör funktionen mer specifik, i det att både implementationen av funktionen och alla anropare av funktionen kommer att behandla den som om funktionen är en metod av en viss typ.

Till exempel:

function addX(this: { x: number }, y: number): number {
  return this.x + y;
}

Funktionen addX() måste bindas till ett objekt med en numerisk x-egenskap för att kunna anropas framgångsrikt. Implementeringen klagar inte på this.x på grund av detta. Det förhindrar att du kan anropa den på ett fristående sätt:

addX(10); // error!  you can't call this function by itself

Istället måste du binda det korrekt... här är ett sätt:

const val = {
  x: 123,
  addX: addX
};
val.addX(10); // okay, you're calling it as a method of type {x: number}

Och om du binder det till fel typ av sak får du också ett fel:

const badVal = {
  x: "whoopsie",
  addX: addX
};
badVal.addX(10); // error! string is not a number, can't do this

Så en funktion med en deklarerad this-parameter är mindre förlåtande kring var den används. Om du har en funktion med en this-parameter och vill anropa den på ett icke godkänt sätt, kan du använda hjälpverktygstypen OmitThisParameter<>:

const unsafeAddX: OmitThisParameter<typeof addX> = addX; // okay
unsafeAddX(10); // no error (but kablooey at runtime, "this is undefined")

Jag skulle nödvändigtvis inte rekommendera att göra detta, men precis som det finns tillfällen då typförsäkningar är användbara, finns det förmodligen tillfällen då det är användbart att ignorera en this-kontextbegränsning.

Så, tillbaka till detta:

  funcRef: OmitThisParameter<() => void>; // 🤔

Det utvärderas till exakt () => void. Typen () => void har ingen this-parameter, så att utelämna den gör ingenting. Det finns egentligen ingen anledning i världen att använda typen OmitThisParameter<() => void>, och jag misstänker att det är ett misstag.

När jag tittar på den klassen och kisar kan jag föreställa mig att göra något sådant här:

export class Example {
  funcRef: OmitThisParameter<Example["func"]>;
  func(this: Example) {}
  constructor() {
    this.funcRef = this.func.bind(this);
  }
}

Jag har lagt till this-begränsningen på func()-metoden (man skulle kunna tro att this-kontexter på faktiska klassmetoder skulle ske automatiskt, men det gör de inte. Saktar nog ner kompilatorn för mycket, antar jag.), och sedan gjorde jag typen av funcRef sådan att den är samma typ som func med begränsningen borttagen. Och det orsakar följande beteende:

const x = new Example();
const func = x.func;
func(); // error! bad this context
const funcRef = x.funcRef;
funcRef(); // okay

Åh ja, hoppas det hjälper. Lycka till!

Länk till kod

Relaterade artiklar