Hallo Bewohner! Das Buch von Dan Vanderkam ist besonders nützlich für diejenigen, die bereits Erfahrung mit JavaScript und TypeScript haben. Der Zweck dieses Buches besteht nicht darin, die Leser in die Verwendung der Tools einzuweisen, sondern ihnen zu helfen, ihre Professionalität zu verbessern. Nachdem Sie es gelesen haben, erhalten Sie ein besseres Verständnis für die Funktionsweise von TypeScript-Komponenten, vermeiden viele Fallstricke und Fallstricke und entwickeln Ihre Fähigkeiten. Während das Referenzhandbuch Ihnen fünf verschiedene Möglichkeiten zeigt, wie Sie die Sprache verwenden können, um dieselbe Aufgabe zu erfüllen, erklärt ein „effektives“ Buch, welche besser ist und warum.
Buchstruktur
Das Buch ist eine Sammlung von kurzen Aufsätzen (Regeln). Die Regeln sind in thematische Abschnitte (Kapitel) unterteilt, auf die je nach Interessenfrage autonom zugegriffen werden kann.
Jede Regelüberschrift enthält einen Tipp. Überprüfen Sie daher das Inhaltsverzeichnis. Wenn Sie beispielsweise eine Dokumentation schreiben und Zweifel haben, ob Typinformationen geschrieben werden sollen, lesen Sie das Inhaltsverzeichnis und Regel 30 („Wiederholen Sie keine Typinformationen in der Dokumentation“).
Fast alle Schlussfolgerungen des Buches werden anhand von Codebeispielen demonstriert. Ich denke, Sie neigen wie ich dazu, technische Bücher zu lesen, indem Sie sich Beispiele ansehen und nur den Textteil durchgehen. Natürlich hoffe ich, dass Sie die Erklärungen sorgfältig lesen, aber ich habe die Hauptpunkte in den Beispielen behandelt.
Nachdem Sie jeden Tipp gelesen haben, können Sie genau verstehen, wie und warum Sie TypeScript effektiver nutzen können. Sie werden auch verstehen, ob es sich in irgendeiner Weise als unbrauchbar herausstellt. Ich erinnere mich an ein Beispiel von Scott Myers, Autor von Effective C ++: Entwickler von Raketensoftware haben möglicherweise Ratschläge zur Verhinderung von Ressourcenlecks vernachlässigt, weil ihre Programme zerstört wurden, als die Rakete das Ziel traf. Mir ist die Existenz von Raketen mit einem in JavaScript geschriebenen Steuerungssystem nicht bekannt, aber eine solche Software ist auf dem James Webb-Teleskop verfügbar. Also sei vorsichtig.
Jede Regel endet mit einem "Must Remember" -Block. Wenn Sie einen kurzen Blick darauf werfen, können Sie sich einen Überblick über das Material verschaffen und die Hauptsache hervorheben. Ich empfehle jedoch dringend, die gesamte Regel zu lesen.
Ein Ausschnitt. REGEL 4. Gewöhnen Sie sich an die strukturelle Typisierung
JavaScript ist unbeabsichtigt vom Typ Ente: Wenn Sie einen Wert mit den richtigen Eigenschaften an eine Funktion übergeben, ist es egal, wie Sie diesen Wert erhalten haben. Sie benutzt es einfach. TypeScript modelliert dieses Verhalten, was manchmal zu unerwarteten Ergebnissen führt, da das Verständnis des Validators für Typen möglicherweise umfassender ist als das Ihre. Wenn Sie die Fähigkeit des strukturierten Schreibens entwickeln, können Sie besser fühlen, wo wirklich Fehler vorliegen, und zuverlässigeren Code schreiben.
Sie arbeiten beispielsweise mit einer Physikbibliothek und haben einen 2D-Vektortyp:
interface Vector2D {
x: number;
y: number;
}
Sie schreiben eine Funktion, um ihre Länge zu berechnen:
function calculateLength(v: Vector2D) {
return Math.sqrt(v.x * v.x + v.y * v.y);
}
und geben Sie die Definition des benannten Vektors ein:
interface NamedVector {
name: string;
x: number;
y: number;
}
Die Funktion berechne Länge funktioniert mit dem NamedVector, da sie die x- und y-Eigenschaften enthält, bei denen es sich um Zahlen handelt. TypeScript versteht dies:
const v: NamedVector = { x: 3, y: 4, name: 'Zee' };
calculateLength(v); // ok, 5.
Das Interessante ist, dass Sie die Beziehung zwischen Vector2D und NamedVector nicht deklariert haben. Sie mussten auch keine alternative berechneLängenausführung für den NamedVector schreiben. Das TypeScript-Typsystem simuliert das Laufzeitverhalten von JavaScript (Regel 1), wodurch der NamedVector berechneLänge aufrufen konnte, basierend auf seiner Struktur, die mit Vector2D vergleichbar ist. Daher der Ausdruck "strukturelle Typisierung".
Es kann aber auch zu Problemen führen. Angenommen, Sie fügen einen 3D-Vektortyp hinzu:
interface Vector3D {
x: number;
y: number;
z: number;
}
und schreibe eine Funktion zum Normalisieren von Vektoren (mache ihre Länge gleich 1):
function normalize(v: Vector3D) {
const length = calculateLength(v);
return {
x: v.x / length,
y: v.y / length,
z: v.z / length,
};
}
Wenn Sie diese Funktion aufrufen, erhalten Sie höchstwahrscheinlich mehr als eine Länge:
> normalize({x: 3, y: 4, z: 5})
{ x: 0.6, y: 0.8, z: 1 }
Was ist schief gelaufen und warum hat TypeScript keinen Fehler gemeldet?
Der Fehler ist, dass berechneLänge mit 2D-Vektoren funktioniert, während Normalisieren mit 3D funktioniert. Daher wird die z-Komponente während der Normalisierung ignoriert.
Es mag seltsam erscheinen, dass die Typprüfung dies nicht erkannt hat. Warum darf berechneLänge für einen 3D-Vektor aufgerufen werden, obwohl sein Typ mit 2D-Vektoren funktioniert?
Was mit named gut funktioniert hat, ist hier nach hinten losgegangen. Das Aufrufen von berechneLänge für das Objekt {x, y, z} löst keinen Fehler aus. Daher beschwert sich das Modul zur Typprüfung nicht, was letztendlich zu einem Fehler führt. Wenn Sie möchten, dass der Fehler in einem solchen Fall erkannt wird, lesen Sie Regel 37.
Wenn Sie Funktionen schreiben, können Sie sich leicht vorstellen, dass sie von den von Ihnen deklarierten Eigenschaften aufgerufen werden und sonst nichts. Dies wird als "versiegelter" oder "exakter" Typ bezeichnet und kann im TypeScript-Typsystem nicht angewendet werden. Ob es Ihnen gefällt oder nicht, die Typen sind hier offen.
Manchmal führt dies zu Überraschungen:
function calculateLengthL1(v: Vector3D) {
let length = 0;
for (const axis of Object.keys(v)) {
const coord = v[axis];
// ~~~~~~~ "any",
// "string"
// "Vector3D"
length += Math.abs(coord);
}
return length;
}
Warum ist das ein Fehler? Da die Achse eine der v Tasten von Vector3D ist, muss sie x, y oder z sein. Und laut der ursprünglichen Vector3D-Erklärung sind dies alles Zahlen. Sollte der Koordinatentyp nicht auch eine Zahl sein?
Dies ist kein falscher Fehler. Wir wissen, dass Vector3D streng definiert ist und keine anderen Eigenschaften hat. Obwohl er konnte:
const vec3D = {x: 3, y: 4, z: 1, address: '123 Broadway'};
calculateLengthL1(vec3D); // ok, NaN
Da v wahrscheinlich irgendwelche Eigenschaften haben könnte, ist die Achse vom Typ Zeichenfolge. Es gibt keinen Grund für TypeScript, v [Achse] nur als Zahl zu betrachten. Beim Durchlaufen von Objekten kann es schwierig sein, eine korrekte Eingabe zu erreichen. Wir werden in Regel 54 auf dieses Thema zurückkommen, aber im Moment verwenden wir keine Schleifen:
function calculateLengthL1(v: Vector3D) {
return Math.abs(v.x) + Math.abs(v.y) + Math.abs(v.z);
}
Strukturelle Typisierung kann auch in Klassen zu Überraschungen führen, die auf mögliche Eigenschaftszuweisungen verglichen werden:
class C {
foo: string;
constructor(foo: string) {
this.foo = foo;
}
}
const c = new C('instance of C');
const d: C = { foo: 'object literal' }; // ok!
Warum kann d C zugewiesen werden? Es hat eine Eigenschaft foo, die Zeichenfolge ist. Es hat auch einen Konstruktor (von Object.prototype), der mit einem Argument aufgerufen werden kann (obwohl es normalerweise ohne dieses Argument aufgerufen wird). Die Strukturen sind also die gleichen. Dies kann zu Überraschungen führen, wenn Sie Logik im C-Konstruktor haben und eine Funktion schreiben, um sie aufzurufen. Dies ist ein wesentlicher Unterschied zu Sprachen wie C ++ oder Java, bei denen Deklarationen eines Parameters vom Typ C sicherstellen, dass er zu C oder einer Unterklasse davon gehört.
Strukturelle Typisierung hilft beim Schreiben von Tests sehr. Angenommen, Sie haben eine Funktion, die eine Datenbankabfrage ausführt und das Ergebnis verarbeitet.
interface Author {
first: string;
last: string;
}
function getAuthors(database: PostgresDB): Author[] {
const authorRows = database.runQuery(`SELECT FIRST, LAST FROM
AUTHORS`);
return authorRows.map(row => ({first: row[0], last: row[1]}));
}
Zum Testen können Sie ein PostgresDB-Modell erstellen. Eine bessere Lösung wäre jedoch, strukturierte Typisierung zu verwenden und eine engere Schnittstelle zu definieren:
interface DB {
runQuery: (sql: string) => any[];
}
function getAuthors(database: DB): Author[] {
const authorRows = database.runQuery(`SELECT FIRST, LAST FROM
AUTHORS`);
return authorRows.map(row => ({first: row[0], last: row[1]}));
}
Sie können postgresDB weiterhin an die Funktion getAuthors in der Ausgabe übergeben, da diese über eine runQuery-Methode verfügt. Die strukturelle Typisierung verpflichtet PostgresDB nicht dazu, zu melden, dass eine Datenbank ausgeführt wird. TypeScript wird es für Sie herausfinden.
Beim Schreiben von Tests können Sie auch ein einfacheres Objekt übergeben:
test('getAuthors', () => {
const authors = getAuthors({
runQuery(sql: string) {
return [['Toni', 'Morrison'], ['Maya', 'Angelou']];
}
});
expect(authors).toEqual([
{first: 'Toni', last: 'Morrison'},
{first: 'Maya', last: 'Angelou'}
]);
});
TypeScript stellt fest, dass die Test-DB der Schnittstelle entspricht. Gleichzeitig benötigen Ihre Tests überhaupt keine Informationen über die Ausgabedatenbank: Es sind keine Scheinbibliotheken erforderlich. Durch die Einführung der Abstraktion (DB) haben wir die Logik von Ausführungsdetails (PostgresDB) befreit.
Ein weiterer Vorteil der strukturellen Typisierung besteht darin, dass Abhängigkeiten zwischen Bibliotheken deutlich aufgehoben werden können. Weitere Informationen zu diesem Thema finden Sie in Regel 51.
BITTE ERINNERN SIE SICH, dass
JavaScript die Ententypisierung verwendet und TypeScript die strukturierte Typisierung modelliert. Infolgedessen haben die Ihren Schnittstellen zugewiesenen Werte möglicherweise Eigenschaften, die in den deklarierten Typen nicht angegeben sind. Typen in TypeScript sind nicht versiegelt.
Beachten Sie, dass Klassen auch strukturelle Typisierungsregeln befolgen. Daher erhalten Sie möglicherweise eine andere Klassenstichprobe als erwartet.
Verwenden Sie die strukturierte Eingabe, um das Testen von Elementen zu vereinfachen.
REGEL 5. Beschränken Sie die Verwendung von Typen
Das Typsystem in TypeScript ist schrittweise und selektiv. Allmählichkeit zeigt sich in der Möglichkeit, dem Code Schritt für Schritt Typen hinzuzufügen, und in der Fähigkeit, das Modul zur Typprüfung bei Bedarf zu deaktivieren. Der zu steuernde Schlüssel ist in diesem Fall der beliebige Typ:
let age: number;
age = '12';
// ~~~ '"12"' 'number'.
age = '12' as any; // ok
Das Berechtigungsmodul, das einen Fehler anzeigt, aber durch einfaches Hinzufügen als beliebiges vermieden werden kann. Wenn Sie mit TypeScript arbeiten, wird es verlockend, Typen oder Aussagen zu verwenden, wenn Sie den Fehler nicht verstehen, dem Validator nicht vertrauen oder keine Zeit damit verbringen möchten, die Typen zu formulieren. Denken Sie jedoch daran, dass jeder viele der Vorteile von TypeScript negiert, nämlich:
Verringert die Codesicherheit
Im obigen Beispiel ist das Alter je nach deklariertem Typ die Zahl. Aber jeder erlaubte es, ihm einen String zuzuweisen. Der Prüfer geht davon aus, dass dies eine Zahl ist (was Sie angegeben haben), was zu Verwirrung führen wird.
age += 1; // ok. age "121".
Ermöglicht das Verletzen von Bedingungen
Beim Erstellen einer Funktion legen Sie eine Bedingung fest, die nach Erhalt eines bestimmten Datentyps von einem Aufruf den entsprechenden Ausgabetyp erzeugt, der wie folgt verletzt wird:
function calculateAge(birthDate: Date): number {
// ...
}
let birthDate: any = '1990-01-19';
calculateAge(birthDate); // ok
Der ParameterirthDate muss Date und nicht string sein. Mit jedem Typ konnte die Bedingung für berechne Alter verletzt werden. Dies kann besonders problematisch sein, da JavaScript dazu neigt, implizite Typkonvertierungen durchzuführen. Aus diesem Grund schlägt die Zeichenfolge in einigen Fällen fehl, in denen die Nummer angegeben werden soll, an anderer Stelle jedoch unweigerlich.
Eliminiert die Unterstützung für einen Sprachdienst
Wenn einem Zeichen ein Typ zugewiesen wird, können TypeScript-Sprachdienste eine geeignete automatische Substitution und kontextbezogene Dokumentation bereitstellen (Abbildung 1.3).

Wenn Sie den Zeichen jedoch den Typ any zuweisen, müssen Sie alles selbst erledigen (Abbildung 1.4).

Und auch umbenennen. Wenn Sie einen Personentyp und Funktionen zum Formatieren des Namens haben:
interface Person {
first: string;
last: string;
}
const formatName = (p: Person) => `${p.first} ${p.last}`;
const formatNameAny = (p: any) => `${p.first} ${p.last}`;
Anschließend können Sie zuerst im Editor markieren und das Symbol umbenennen auswählen, um es in Vorname umzubenennen (Abbildung 1.5 und Abbildung 1.6).
Dies ändert die formatName-Funktion, jedoch nicht bei:
interface Person {
first: string;
last: string;
}
const formatName = (p: Person) => `${p.firstName} ${p.last}`;
const formatNameAny = (p: any) => `${p.first} ${p.last}`;

»Weitere Details zum Buch finden Sie auf der Website des Herausgebers.
» Inhaltsverzeichnis
» Auszug
für Einwohner 25% Rabatt auf den Gutschein - TypeScript
Nach Zahlung der Papierversion des Buches wird ein E-Book an die E-Mail gesendet.