Als ich das Wort zum ersten Mal nie sah, dachte ich, wie nutzlos der Typ in TypeScript erscheint. Mit der Zeit begann ich zu verstehen, wie mächtig dieses Wort ist. Und diese Kraft entsteht aus realen Anwendungsfällen, die ich mit dem Leser teilen möchte. Wen kümmert es, willkommen bei Katze.
Was ist nie
Wenn wir in die Geschichte eintauchen, werden wir feststellen, dass der Typ zu Beginn der TypeScript-Version 2.0 nie aufgetaucht ist, mit einer eher
bescheidenen Beschreibung seines Zwecks. Wenn Sie die Version der ts-Entwickler kurz und frei nacherzählen, ist der Typ niemals ein primitiver Typ, der ein Zeichen für Werte darstellt, die niemals auftreten werden. Oder ein Vorzeichen für Funktionen, die niemals Werte zurückgeben, entweder aufgrund ihrer Schleife, beispielsweise einer Endlosschleife, oder aufgrund ihrer Unterbrechung. Und um die Essenz des Gesagten klar zu zeigen, schlage ich vor, unten ein Beispiel zu sehen:
function error(message: string): never { throw new Error(message); } function infiniteLoop(): never { while (true) { } } function infiniteRec(): never { return infiniteRec(); }
Vielleicht habe ich aufgrund solcher Beispiele den ersten Eindruck bekommen, dass der Typ zur Klarheit benötigt wird.
Typ System
Jetzt kann ich sagen, dass die reichhaltige Fauna des Typensystems auch in TypeScript niemals fällig ist. Und zur Unterstützung meiner Worte werde ich verschiedene Bibliothekstypen aus lib.es5.d.ts angeben
type Exclude<T, U> = T extends U ? never : T; type Extract<T, U> = T extends U ? T : never; type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; type NonNullable<T> = T extends null | undefined ? never : T; type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
Von meinen Typen mit nie werde ich meinen Favoriten geben - GetNames, ein verbessertes Analogon von keyof:
type GetNames<FromType, KeepType = any, Include = true> = { [K in keyof FromType]: FromType[K] extends KeepType ? Include extends true ? K : never : Include extends true ? never : K }[keyof FromType];
Überwachung zukünftiger Änderungen
Wenn das Leben des Projekts nicht flüchtig ist, nimmt die Entwicklung einen besonderen Charakter an. Ich möchte die Kontrolle über wichtige Punkte im Code haben, um sicherzustellen, dass Sie oder Ihre Teammitglieder nicht vergessen, einen wichtigen Code zu reparieren, wenn es Zeit braucht. Solche Codeabschnitte sind besonders offensichtlich, wenn ein Zustand oder eine Aufzählung von etwas vorliegt. Beispiel: Auflisten einer Reihe von Aktionen für eine Entität. Ich schlage vor, ein Beispiel zu betrachten, um den Kontext des Geschehens zu verstehen.
Der obige Code wird so weit wie möglich vereinfacht, um sich nur auf einen wichtigen Punkt zu konzentrieren. Der AdminAction-Typ wird in einem anderen Projekt definiert und es ist sogar möglich, dass er nicht von Ihrem Team begleitet wird. Da das Projekt eine lange Lebensdauer hat, ist es erforderlich, Ihre ActionEngine ohne Ihr Wissen vor Änderungen im AdminAction-Typ zu schützen. TypeScript bietet verschiedene Rezepte zur Lösung dieses Problems. Eines davon ist die Verwendung des Never-Typs. Dazu müssen wir einen NeverError definieren und in der doAction-Methode verwenden.
class NeverError extends Error {
Fügen Sie nun AdminAction einen neuen "BLOCK" -Wert hinzu und erhalten Sie beim Kompilieren eine Fehlermeldung: Das Argument vom Typ "BLOCK" kann nicht dem Parameter vom Typ "never'.ts (2345) zugewiesen werden.
Im Prinzip haben wir dies erreicht. Es ist erwähnenswert, dass die Schalterkonstruktion uns davor schützt, AdminAction-Elemente zu ändern oder aus dem Set zu entfernen. Aus der Praxis kann ich sagen, dass es wirklich wie erwartet funktioniert.
Wenn Sie die NeverError-Klasse nicht einführen möchten, können Sie den Code steuern, indem Sie eine Variable vom Typ never deklarieren. So:
type AdminAction = "CREATE" | "ACTIVATE" | "BLOCK"; class ActionEngine { doAction(action: AdminAction) { switch (action) { case "CREATE":
Kontextlimit: dies + nie
Der folgende Trick bewahrt mich oft vor lächerlichen Fehlern inmitten von Müdigkeit oder Nachlässigkeit. Im folgenden Beispiel werde ich keine Bewertung der Qualität des gewählten Ansatzes abgeben. Bei uns passiert das de facto. Angenommen, Sie verwenden eine Methode in einer Klasse, die keinen Zugriff auf die Felder der Klasse hat. Ja, es klingt beängstigend - das alles ist Regierungscode.
@SomeDecorator({...}) class SomeUiPanel { @Inject private someService: SomeService; public beforeAccessHook() {
In einem breiteren Fall können es Rückruf- oder Pfeilfunktionen sein, die ihre eigenen Ausführungskontexte haben. Und die Aufgabe ist: Wie schützen Sie sich vor Laufzeitfehlern? Zu diesem Zweck kann TypeScript
diesen Kontext angeben.
@SomeDecorator({...}) class SomeUiPanel { @Inject private someService: SomeService; public beforeAccessHook(this: never) {
Fairerweise werde ich sagen, dass es niemals verdient. Stattdessen können Sie void und {} verwenden. Aber es ist der Typ, der niemals Aufmerksamkeit erregt, wenn Sie den Code lesen.
Erwartungen
Invarianten
Da ich eine klare Vorstellung von nie hatte, dachte ich, dass der folgende Code funktionieren sollte:
type Maybe<T> = T | void; function invariant<Cond extends boolean>(condition: Cond, message: string): Cond extends true ? void : never { if (condition) { return; } throw new Error(message); } function f(x: Maybe<number>, c: number) { if (c > 0) { invariant(typeof x === "number", "When c is positive, x should be number"); (x + 1);
Aber leider. Der Ausdruck (x + 1) gibt einen Fehler aus: Der Operator '+' kann nicht auf die Typen 'Vielleicht' und '1' angewendet werden. Das Beispiel selbst habe ich im Artikel
Übertragen von 30.000 Codezeilen von Flow nach TypeScript ausspioniert
.Flexible Bindung
Ich dachte, dass ich mit Hilfe von nie die obligatorischen Funktionsparameter steuern und unter bestimmten Bedingungen unnötige deaktivieren kann. Aber nein, das wird nicht funktionieren:
function variants<Type extends number | string>(x: Type, c: Type extends number ? number : never): number { if (typeof x === "number") { return x + c; } return +x; } const three = variants(1, 2);
Das obige Problem wird auf
andere Weise gelöst.
Strengere Überprüfung
Ich wollte, dass der ts-Compiler so etwas nicht verpasst, was dem gesunden Menschenverstand widerspricht.
variants(<never> {}, <never> {});
Fazit
Am Ende möchte ich eine kleine Aufgabe aus einer Reihe seltsamer Kuriositäten anbieten. Welche Zeile ist der Fehler?
class never<never> { never: never; } const whats = new never<string>(); whats.never = "";
OptionIn letzterem: Der Typ '""' kann nicht dem Typ 'never'.ts (2322) zugewiesen werden.
Das ist alles, worüber ich niemals erzählen wollte. Vielen Dank für Ihre Aufmerksamkeit und bis bald.