TypeScript vereinfacht Entwicklern das Schreiben von Code, der nicht abstürzt, indem es Typfehler noch vor der Ausführung des Programms abfängt. Wir möchten, dass Entwickler die Vorteile dieses Tools nutzen und haben deshalb vor einem Jahr ein System zur automatischen Generierung von TypeScript-Typen für die Cloudflare Workers Runtime entwickelt. Dadurch konnten Entwickler in ihren IDEs Code-Vervollständigungen für Workers-APIs sehen und den Code vor der Bereitstellung auf seinen Typ prüfen. Jede Woche erschien eine neue Version der Typen, die die neuesten Änderungen.
Im Laufe des letzten Jahres gaben uns unseren Kunden und internen Teams viel Feedback zur Verbesserung unserer Typen. Mit der Umstellung auf das Bazel-Build-System in Vorbereitung auf das Opensourcing der Runtime sahen wir die Gelegenheit, unsere Typen so umzubauen, dass sie genauer, einfacher zu verwenden und einfacher zu generieren sind. Heute kündigen wir die nächste große Version von @cloudflare/workers-types
an – mit einer Reihe neuer Features und dem Opensourcing der vollständig neu geschriebenen automatischen Generierungsskripte.
So verwenden Sie TypeScript mit Workers
Die Einrichtung von TypeScript in Workers ist ganz einfach! Wenn Sie zum ersten Mal mit Workers arbeiten, installieren Sie Node.js und führen Sie dann npx wrangler init
in Ihrem Terminal aus, um ein neues Projekt zu generieren. Wenn Sie ein bestehendes Workers-Projekt haben und unsere verbesserten Typisierungen nutzen möchten, installieren Sie die neuesten Versionen von TypeScript und @cloudflare/workers-types
mit npm install --save-dev typescript @cloudflare/workers-types@latest
und erstellen Sie dann eine tsconfig.json
-Datei mit folgendem Inhalt:
Ihr Editor wird jetzt Probleme hervorheben und Ihnen während der Eingabe Code-Vervollständigungen vorschlagen. So machen Sie weniger Fehler und die Arbeit wird angenehmer.
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": ["esnext"],
"types": ["@cloudflare/workers-types"]
}
}
Editor hebt die falsche Verwendung von set
anstelle von put
hervor und bietet Codevervollständigungen
Verbesserte Interoperabilität mit Standardtypen
Cloudflare Workers implementieren viele der gleichen Laufzeit-APIs wie Browser, und wir verbessern ständig unsere Standardkonformität mit dem WinterCG. So können Browser beispielsweise Audiodateien abspielen, während Worker direkten Zugriff auf das Netzwerk von Cloudflare haben, um global verteilte Daten zu speichern. Diese Diskrepanz bedeutet: Die Runtime-APIs und Typen, die von jeder Plattform bereitgestellt werden, sind unterschiedlich. Es ist daher schwierig, Workers-Typen mit Frameworks wie Remix zu verwenden, die die gleichen Dateien im Cloudflare-Netzwerk und im Browser ausführen. Diese Dateien müssen gegen lib.dom.d.ts
typgeprüft werden, was mit unseren Typen nicht kompatibel ist.
Als Lösung für dieses Problem generieren wir jetzt eine separate Version unserer Typen. Sie kann selektiv importiert werden, ohne dass Sie @cloudflare/workers-types
in das Feld types
in Ihrer tsconfig.json
aufnehmen müssen. So könnte das aussehen:
Außerdem erzeugen wir automatisch ein diff unserer Typen gegen die lib.webworker.d.ts
von TypeScript. In Zukunft werden wir anhand dieser Informationen Bereiche identifizieren, in denen wir die Spec-compliance weiter verbessern können.
import type { KVNamespace } from "@cloudflare/workers-types";
declare const USERS_NAMESPACE: KVNamespace;
Verbesserte Kompatibilität mit Kompatibilitätsdaten
Cloudflare gibt starke Zusagen zur Abwärtskompatibilität für alle von uns bereitgestellten APIs. Wir verwenden Kompatibilitätsflags und Datumsangaben, um wichtige Änderungen auf abwärtskompatible Weise vorzunehmen. Manchmal ändern diese Kompatibilitätsflags die Typen. Zum Beispiel fügt das global_navigator
-Flag einen neuen globalen navigator
hinzu, und das url_standard
-Flag ändert die URLSearchParams
-Konstruktorsignatur.
Sie können jetzt die Version der Typen auswählen, die Ihrem Kompatibilitätsdatum entspricht. So können Sie sicher sein, dass Sie keine Features verwenden, die nicht in Runtime unterstützt werden.
Verbesserte Integration mit Wrangler
{
"compilerOptions": {
...
"types": ["@cloudflare/workers-types/2022-08-04"]
}
}
Neben den Kompatibilitätsdaten wirkt sich die Konfiguration Ihrer Worker-Umgebung auch auf die Runtime- und Type-API-Oberfläche aus. Wenn Sie in Ihrer wrangler.toml
Bindings wie KV-Namespaces oder R2-Buckets konfiguriert haben, müssen sich diese in TypeScript-Typen widerspiegeln. Ebenso müssen benutzerdefinierte Text-, Daten- und WebAssembly-Modulregeln deklariert werden, damit TypeScript die Exporttypen kennt. Bisher konnten Sie eine separate TypeScript-Umgebungsdatei mit diesen Deklarationen erstellen.
Um wrangler.toml
als Single Source of Truth zu erhalten, können Sie jetzt npx wrangler types
ausführen, um diese Datei automatisch zu erzeugen.
Zum Beispiel generiert die folgende wrangler.toml
...
... diese Ambiet-Typen:
kv_namespaces = [{ binding = "MY_NAMESPACE", id = "..." }]
rules = [{ type = "Text", globs = ["**/*.txt"] }]
Verbesserte integrierte Dokumentation und Änderungsprotokolle
interface Env {
MY_NAMESPACE: KVNamespace;
}
declare module "*.txt" {
const value: string;
export default value;
}
Codevervollständigungen bieten Entwicklern, die neu auf der Workers-Plattform sind, eine hervorragende Möglichkeit, die API-Oberfläche zu erkunden. Wir nehmen jetzt die Dokumentation für Standard-APIs aus den offiziellen TypeScript-Typen in unsere Typen auf. Wir beginnen auch die Integration von Dokumentationen für cloudflare-spezifische APIs in diese Typen.
Entwickler, die bereits mit der Workers-Plattform arbeiten, können möglicherweise nur schwer erkennen, wie sich die Typen mit jedem Release von @cloudflare/workers-types
ändern. Um Typfehler zu vermeiden und neue Features hervorzuheben, generieren wir jetzt mit jeder Version ein detailliertes Änderungsprotokoll, in dem wir die neuen, geänderten und entfernten Definitionen aufschlüsseln.
Wie funktioniert die Typgenerierung hinter den Kulissen?
Wie bereits erwähnt, haben wir die Skripte zur automatischen Typgenerierung komplett überarbeitet. Jetzt sind sie noch zuverlässiger, erweiterungsfähiger und wartungsfreundlicher. Das bedeutet, dass Entwickler verbesserte Typen erhalten, sobald neue Versionen der Runtime veröffentlicht werden. Unser System verwendet jetzt das neue Runtime-Typ-Informationssystem (Runtime-type-information oder RTTI) von workerd
, um die Typen von Workers Runtime-APIs abzufragen, anstatt zu versuchen, diese Informationen aus geparsten C++ ASTs zu extrahieren.
Anschließend übergeben wir diese RTTI an ein TypeScript-Programm, mit Hilfe der TypeScript-Compiler-API erzeugt es Deklarationen und führt AST-Transformationen durch, um sie zu bereinigen. Dies ist in das Bazel-Build-System von workerd
integriert, d. h. die Erzeugung von Typen ist jetzt ein einziger bazel build //types:types
-Befehl. Wir nutzen den Cache von Bazel, um so wenig wie möglich während der Generierung neu zu erstellen.
// Encode the KV namespace type without any compatibility flags enabled
CompatibilityFlags::Reader flags = {};
auto builder = rtti::Builder(flags);
auto type = builder.structure<KvNamespace>();
capnp::TextCodec codec;
auto encoded = codec.encode(type);
KJ_DBG(encoded); // (name = "KvNamespace", members = [ ... ], ...)
Während die automatisch generierten Typen die JavaScript-Schnittstelle von Workers Runtime-APIs korrekt beschreiben, bietet TypeScript zusätzliche Features, mit denen wir Typen mit höherer Zuverlässigkeit bereitstellen und Entwickelung ergonomischer gestalten können. Unser System ermöglicht es uns, partielle TypeScript-„Überschreibungen“ von Hand zu schreiben, die mit den automatisch generierten Typen zusammengeführt werden. Dadurch können wir:
import ts, { factory as f } from "typescript";
const keyParameter = f.createParameterDeclaration(
/* decorators */ undefined,
/* modifiers */ undefined,
/* dotDotDotToken */ undefined,
"key",
/* questionToken */ undefined,
f.createTypeReferenceNode("string")
);
const returnType = f.createTypeReferenceNode("Promise", [
f.createUnionTypeNode([
f.createTypeReferenceNode("string"),
f.createLiteralTypeNode(f.createNull()),
]),
]);
const getMethod = f.createMethodSignature(
/* modifiers */ undefined,
"get",
/* questionToken */ undefined,
/* typeParameters */ undefined,
[keyParameter],
returnType
);
const kvNamespace = f.createInterfaceDeclaration(
/* decorators */ undefined,
/* modifiers */ undefined,
"KVNamespace",
/* typeParameters */ undefined,
/* heritageClauses */ undefined,
[getMethod]
);
const file = ts.createSourceFile("file.ts", "", ts.ScriptTarget.ESNext);
const printer = ts.createPrinter();
const output = printer.printNode(ts.EmitHint.Unspecified, kvNamespace, file);
console.log(output); // interface KVNamespace { get(key: string): Promise<string | null>; }
Typparameter (Generika) zu Typen wie
ReadableStream
hinzufügen undany
(jegliche) typisierten Werte vermeiden.die Korrespondenz zwischen Eingabe- und Ausgabetypen mit Methodenüberladungen festlegen.
KVNamespace#get()
sollte zum Beispiel einenstring
zurückgeben, wenn dastype
-Argumenttext
lautet, aberArrayBuffer
, wenn esarrayBuffer
ist.Typen umbenennen, um den TypeScript-Standards zu entsprechen und die Ausführlichkeit zu reduzieren.
einen Typ vollständig ersetzen, um genauere Deklarationen zu erhalten. z. B. ersetzen wir
WebSocketPair
durch eineconst
-Deklaration für bessere Typen mitObject.values()
.Typen für Werte bereitstellen, die intern nicht typisiert sind, wie z. B. das
Request#cf
-Objekt.interne Typen ausblenden, die in Ihren Workern nicht verwendbar sind.
Zuvor wurden diese Überschreibungen in separaten TypeScript-Dateien zu den „C++“-Deklarationen definiert, die sie überschrieben. Somit stimmten sie oft nicht mehr mit den ursprünglichen Deklarationen überein. Im neuen System werden die Überschreibungen zusammen mit den Ursprüngen mit „C++“-Makros definiert. Also können sie jetzt zusammen mit Änderungen an der Runtime-Implementierung überprüft werden. In der README für den JavaScript-Glue-Code von workerd
finden Sie viele weitere Details und Beispiele.
Probieren Sie Workers-Types noch heute!
Wir empfehlen Ihnen ein Upgrade auf die neueste Version von @cloudflare/workers-types
mit npm install --save-dev @cloudflare/workers-types@latest
, und probieren Sie den neuen Befehl wrangler types
aus. Wir werden mit jedem workerd
-Release eine neue Version der Typen veröffentlichen. Berichten Sie uns im Cloudflare Developers Discord, was Sie darüber denken. Und wenn Sie verbesserungswürdige Typen finden, öffnen Sie bitte ein GitHub-Issue.