Mit TypeScript können Sie viele Aufgaben automatisieren, die Entwickler ohne diese Sprache unabhängig lösen müssen. Bei der Arbeit mit TypeScript müssen jedoch keine Typanmerkungen ständig verwendet werden. Tatsache ist, dass der Compiler eine hervorragende Arbeit bei der Typinferenz leistet, die auf dem Kontext der Codeausführung basiert. Der Artikel, dessen Übersetzung wir heute veröffentlichen, widmet sich ziemlich komplizierten Fällen von Typinferenz, in denen das Schlüsselwort infer
und die Konstruktion verwendet werden as const
.
Grundlagen der Typinferenz
Schauen Sie sich zunächst das einfachste Beispiel für eine Typinferenz an.let variable;
Eine auf diese Weise deklarierte Variable ist vom Typ any
. Wir haben dem Compiler keine Hinweise gegeben, wie wir ihn verwenden werden.let variable = 'Hello!';
Hier haben wir eine Variable deklariert und sofort einen Wert in sie geschrieben. TypeScript kann nun erraten, dass diese Variable vom Typ ist string
, sodass wir jetzt eine vollkommen akzeptable typisierte Variable haben.Ein ähnlicher Ansatz gilt für Funktionen:function getRandomInteger(max: number) {
return Math.floor(Math.random() * max);
}
In diesem Code geben wir nicht an, dass die Funktion getRandomInteger
eine Zahl zurückgibt. Aber der TypeScript-Compiler weiß das sehr gut.Typinferenz in Generika
Die obigen Konzepte beziehen sich auf universelle Typen (Generika). Wenn Sie mehr über Generika erfahren möchten, schauen Sie sich dieses und dieses Material an.Beim Erstellen generischer Typen können Sie viele nützliche Dinge tun. Die Typinferenz erleichtert das Arbeiten mit universellen Typen und vereinfacht sie.function getProperty<ObjectType, KeyType extends keyof ObjectType>(
object: ObjectType, key: KeyType
) {
return object[key];
}
Bei Verwendung der obigen generischen Funktion müssen die Typen nicht explizit angegeben werden.const dog = {
name: 'Fluffy'
};
getProperty(dog, 'name');
Diese Technik ist unter anderem sehr nützlich bei der Erstellung universeller React-Komponenten. Hier ist das Material dazu.Verwenden Sie das Schlüsselwort infer
Eine der fortschrittlichsten TypeScript-Funktionen, die bei der Typinferenz in den Sinn kommen, ist das Schlüsselwort infer
.Betrachten Sie ein Beispiel. Erstellen Sie die folgende Funktion:function call<ReturnType>(
functionToCall: (...args: any[]) => ReturnType, ...args: any[]
): ReturnType {
return functionToCall(...args);
}
Rufen Sie mit Hilfe dieser Funktion eine andere Funktion auf und schreiben Sie, was sie zurückgibt, in eine Konstante:const randomNumber = call(getRandomInteger, 100);
Mit dem vorherigen Ausdruck können wir ermitteln, welche Funktion getRandomInteger
die empfangene Eingabe als Obergrenze der an sie zurückgegebenen zufälligen Ganzzahl 100 zurückgegeben hat. Richtig, es gibt ein kleines Problem. Es liegt in der Tatsache, dass uns nichts daran hindert, die Arten von Funktionsargumenten zu ignorieren getRandomInteger
.const randomNumber = call(getRandomInteger, '100');
Da TypeScript Spread- und Rest-Parameter in Funktionen höherer Ordnung unterstützt, können wir dieses Problem folgendermaßen lösen:function call<ArgumentsType extends any[], ReturnType>(
functionToCall: (...args: ArgumentsType) => ReturnType, ...args: ArgumentsType
): ReturnType {
return functionToCall(...args);
}
Jetzt haben wir darauf hingewiesen, dass die Funktion call
ein Array von Argumenten in beliebiger Form verarbeiten kann und dass die Argumente den Erwartungen der an sie übergebenen Funktion entsprechen müssen.Versuchen wir nun erneut, einen falschen Funktionsaufruf durchzuführen:const randomNumber = call(getRandomInteger, '100');
Dies führt zu einer Fehlermeldung:Argument of type ‘”100″‘ is not assignable to parameter of type ‘number’.
In der Tat haben wir gemäß den obigen Schritten einfach ein Tupel erstellt. Tupel in TypeScript sind Arrays mit fester Länge, deren Werttypen bekannt sind, aber nicht identisch sein müssen.type Option = [string, boolean];
const option: Option = ['lowercase', true];
Keyword-Funktionen schließen
Stellen wir uns nun vor, dass unser Ziel nicht darin besteht, die Rückgabe der Funktion zu erhalten, sondern nur Informationen über die Art der an sie zurückgegebenen Daten.type FunctionReturnType<FunctionType extends (...args: any) => ?> = ?;
Der oben genannte Typ ist noch nicht einsatzbereit. Wir müssen das Problem lösen, wie der Rückgabewert ermittelt werden kann. Hier können Sie alles manuell beschreiben, aber dies widerspricht unserem Ziel.type FunctionReturnType<ReturnType, FunctionType extends (...args: any) => ReturnType> = ReturnType;
FunctionReturnType<number, typeof getRandomInteger>;
Anstatt dies alleine zu tun, können wir TypeScript bitten, den Rückgabetyp auszugeben. Das Schlüsselwort infer
kann nur in bedingten Typen verwendet werden. Deshalb kann unser Code manchmal etwas unordentlich sein.type FunctionReturnType<FunctionType extends (args: any) => any> = FunctionType extends (...args: any) => infer ReturnType ? ReturnType : any;
Folgendes passiert in diesem Code:- Hier heißt es zu
FunctionType
expandieren (args: any) => any
. - Wir weisen darauf hin, dass
FunctionReturnType
dies ein bedingter Typ ist. - Wir prüfen, ob es sich ausdehnt
FunctionType (...args: any) => infer ReturnType
.
Nachdem wir dies alles getan haben, können wir den Rückgabetyp jeder Funktion extrahieren.FunctionReturnType<typeof getRandomInteger>;
Das Obige ist eine so häufige Aufgabe, dass TypeScript über ein integriertes Dienstprogramm ReturnType verfügt , mit dem dieses Problem gelöst werden soll.Konstruiere als const
Ein weiteres Problem Typinferenz Zusammenhang ist der Unterschied zwischen den Schlüsselwörtern const
und let
die verwendet werden , wenn Konstanten und Variablen zu deklarieren.let fruit = 'Banana';
const carrot = 'Carrot';
Variable fruit
- hat einen Typ string
. Dies bedeutet, dass jeder Zeichenfolgenwert darin gespeichert werden kann.Und eine Konstante carrot
ist ein String-Literal. Es kann als Beispiel für einen Subtyp betrachtet werden string
. Die folgende Beschreibung von Zeichenfolgenliteralen wird in dieser PR gegeben : "Das Typzeichenfolgenliteral ist ein Typ, dessen erwarteter Wert eine Zeichenfolge ist, deren Textinhalt dem gleichen Inhalt des Zeichenfolgenliteral entspricht."Dieses Verhalten kann geändert werden. TypeScript 3.4 führt eine interessante neue Funktion namens const assertions ein, die die Verwendung eines Konstrukts ermöglicht as const
. So sieht seine Verwendung aus:let fruit = 'Banana' as const;
Jetzt ist fruit
es ein String-Literal. Das Design as const
ist auch praktisch, wenn eine Entität unveränderlich gemacht werden muss. Betrachten Sie das folgende Objekt:const user = {
name: 'John',
role: 'admin'
};
In JavaScript const
bedeutet das Schlüsselwort , dass Sie nicht überschreiben können, was in einer Konstante gespeichert ist user
. Andererseits können Sie die interne Struktur eines in dieser Konstante aufgezeichneten Objekts ändern.Jetzt speichert das Objekt die folgenden Typen:const user: {
name: string,
role: string
};
Damit das System dieses Objekt als unveränderlich wahrnimmt, können Sie Folgendes verwenden as const
:const user = {
name: 'John',
role: 'admin'
} as const;
Jetzt haben sich die Typen geändert. Strings wurden zu String-Literalen, nicht zu gewöhnlichen Strings. Aber nicht nur das hat sich geändert. Jetzt sind die Eigenschaften schreibgeschützt:const user: {
readonly name: 'John',
readonly role: 'admin'
};
Und wenn wir mit Arrays arbeiten, eröffnen sich uns noch mächtigere Möglichkeiten:const list = ['one', 'two', 3, 4];
Der Typ dieses Arrays ist (string | number)[]
. Mit diesem Array können as const
Sie es in ein Tupel verwandeln:const list = ['one', 'two', 3, 4] as const;
Der Typ dieses Arrays sieht nun folgendermaßen aus:readonly ['one', 'two', 3, 4]
All dies gilt für komplexere Strukturen. Betrachten Sie das Beispiel, das Anders Halesberg in seiner Rede auf der TSConf 2019 gegeben hat :const colors = [
{ color: 'red', code: { rgb: [255, 0, 0], hex: '#FF0000' } },
{ color: 'green', code: { rgb: [0, 255, 0], hex: '#00FF00' } },
{ color: 'blue', code: { rgb: [0, 0, 255], hex: '#0000FF' } },
] as const;
Unser Array ist colors
jetzt vor Änderungen geschützt, und seine Elemente sind auch vor Änderungen geschützt:const colors: readonly [
{
readonly color: 'red';
readonly code: {
readonly rgb: readonly [255, 0, 0];
readonly hex: '#FF0000';
};
},
]
Zusammenfassung
In diesem Artikel haben wir uns einige Beispiele für die Verwendung erweiterter Typinferenzmechanismen in TypeScript angesehen. Das Schlüsselwort infer
und der Mechanismus werden hier verwendet as const
. Diese Tools können in besonders schwierigen Situationen sehr nützlich sein. Zum Beispiel, wenn Sie mit unveränderlichen Entitäten arbeiten müssen oder wenn Sie Programme in einem funktionalen Stil schreiben. Wenn Sie sich weiterhin mit diesem Thema vertraut machen möchten, schauen Sie sich dieses Material an.Liebe Leser! Verwenden Sie Schlüsselwörter infer
und Konstruktionen as const
in TypeScript?