Subscribe to receive notifications of new posts:

Améliorer la prise en charge de TypeScript sur Workers : précision, ergonomie et interopérabilité

11/18/2022

7 min read
Improving Workers TypeScript support: accuracy, ergonomics and interoperability

TypeScript permet aux développeurs d'écrire facilement du code qui ne provoque pas de défaillances en détectant les erreurs de type avant l'exécution de votre programme. Nous voulons permettre aux développeurs de profiter de cet outil ; c'est pourquoi, il y a un an de cela, nous avons construit un système permettant de générer automatiquement des types TypeScript pour le runtime de Cloudflare Workers. Celui-ci permettait aux développeurs d'afficher des suggestions de code dans leurs IDE pour les API Workers et de vérifier l'intégrité du code avant son déploiement. Chaque semaine, une nouvelle version des types était publiée, reflétant les modifications les plus récentes.

Au cours de l'année passée, nous avons reçu de nombreux commentaires de la part de clients et de nos équipes internes concernant les améliorations que nous pourrions apporter à nos types. Avec l'adoption du système de version Bazel en préparation de la mise en disponibilité open source du runtime, nous avons vu une opportunité de reconstruire nos types afin qu'ils soient plus précis, plus faciles à utiliser et plus simples à générer. Aujourd'hui, nous sommes heureux d'annoncer la prochaine version majeure de @cloudflare/workers-types avec une pléthore de nouvelles fonctionnalités, ainsi que la mise en disponibilité open source de scripts de génération automatique entièrement réécrits.

Comment utiliser TypeScript avec Workers

Configurer TypeScript dans Workers, c'est facile ! Si vous faites vos premiers pas avec Workers, installez Node.js, puis exécutez la commande npx wrangler init sur votre terminal pour générer un nouveau projet. Si vous disposez d'un projet Workers existant et vous souhaitez profiter de nos types améliorés, installez les toutes dernières versions de TypeScript et de @cloudflare/workers-types avec la commande npm install --save-dev typescript @cloudflare/workers-types@latest, puis créez un fichier tsconfig.json avec le contenu suivant :

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "lib": ["esnext"],
    "types": ["@cloudflare/workers-types"]
  }
}

Votre éditeur met maintenant en évidence les problèmes et vous propose des suggestions de code au fur et à mesure de la saisie, rendant votre expérience de développeur moins sujette aux erreurs et plus agréable.

Éditeur mettant en évidence l'utilisation incorrecte de set au lieu de put et fournissant des suggestions de code

Amélioration de l'interopérabilité avec les types standard

Cloudflare Workers met en œuvre, dans une large mesure, les mêmes API de runtime que les navigateurs, et nous nous employons à améliorer encore notre conformité aux normes avec WinterCG. Cependant, il subsistera toujours des différences fondamentales entre ce que peuvent faire les navigateurs et Workers. Par exemple, les navigateurs peuvent lire des fichiers audio, tandis que les instances Workers disposent d'un accès direct au réseau de Cloudflare pour stocker des données distribuées dans le monde entier. Ce décalage signifie que les API de runtime et les types fournis par chaque plateforme sont différents, rendant difficile l'utilisation des types Workers avec des frameworks (tels que Remix) qui exécutent les mêmes fichiers sur le réseau Cloudflare et dans le navigateur. Ces fichiers doivent faire l'objet d'une vérification contre lib.dom.d.ts, qui est incompatible avec nos types.

Pour résoudre ce problème, nous générons maintenant une version distincte de nos types, qui peut être importée de manière sélective, sans devoir inclure @cloudflare/workers-types dans le champ types de votre fichier tsconfig.json. Voici un exemple de ce que cela donne :

import type { KVNamespace } from "@cloudflare/workers-types";

declare const USERS_NAMESPACE: KVNamespace;

En outre, nous générons automatiquement un diff de nos types en fonction u fichier lib.webworker.d.ts de TypeScript. À l'avenir, nous utiliserons ces données pour identifier les domaines dans lesquels nous pouvons encore améliorer notre conformité spec.

Amélioration de la compatibilité avec les dates de compatibilité

Cloudflare tient de solides promesses de rétrocompatibilité pour toutes les API fournies. Nous utilisons des indicateurs et des dates de compatibilité pour introduire d'importants changements de manière rétrocompatible. Parfois, ces indicateurs de compatibilité modifient les types. Par exemple, l'indicateur global_navigator ajoute un nouvel objet global navigator, et l'indicateur url_standard modifie la signature de constructeur URLSearchParams.

Nous vous permettons désormais de sélectionner la version des types qui correspond à votre date de compatibilité, afin que vous ayez l'assurance de ne pas utiliser de fonctionnalités qui ne seront pas prises en charge lors de l'exécution.

{
  "compilerOptions": {
    ...
    "types": ["@cloudflare/workers-types/2022-08-04"]
  }
}

Amélioration de l'intégration avec Wrangler

En plus des dates de compatibilité, la configuration de l'environnement de votre instance Workers affecte également le runtime et la surface de l'API de type. Si vous avez configuré des liaisons telles que les espaces de noms KV ou les buckets R2 dans votre fichier wrangler.toml, celles-ci doivent être reflétées dans les types TypeScript. De même, les règles relatives aux textes personnalisés, aux données et au module WebAssembly doivent être déclarées pour que TypeScript ait connaissance des types d'exportations. Auparavant, c'est à vous qu'il incombait de créer un fichier ambient TypeScript distinct contenant ces déclarations.

Pour vous assurer que le fichier wrangler.toml reste la source unique de données exactes, vous pouvez maintenant exécuter la commande npx wrangler types afin de générer ce fichier automatiquement.

Par exemple, le fichier wrangler.toml suivant…

kv_namespaces = [{ binding = "MY_NAMESPACE", id = "..." }]
rules = [{ type = "Text", globs = ["**/*.txt"] }]

…génère les types ambient suivants :

interface Env {
  MY_NAMESPACE: KVNamespace;
}
declare module "*.txt" {
  const value: string;
  export default value;
}

Amélioration de la documentation intégrée et des journaux de modifications

Les suggestions de code offrent aux développeurs effectuant leurs premiers pas sur la plateforme Workers un excellent moyen d'explorer la surface des API. Nous incluons maintenant dans nos types la documentation des API standard issue des types officiels de TypeScript. Nous commençons également à y intégrer des documentations relatives aux API spécifiques de Cloudflare.

docs in types shown with code completions

Pour les développeurs utilisant déjà la plateforme Workers, il peut être difficile de suivre les modifications des types à chaque nouvelle version de @cloudflare/workers-types. Afin d'éviter les erreurs de type et de mettre en évidence les nouvelles fonctionnalités, nous générons maintenant avec chaque version un journal de modifications détaillé qui différencie clairement les définitions nouvelles, modifiées et supprimées.

Comment fonctionnent les rouages de la génération de types ?

Comme nous l'avons indiqué plus haut, nous avons entièrement reconstruit les scripts de génération automatique de types afin de les rendre plus fiables et extensibles et d'en faciliter la maintenance. Cela signifie que les développeurs recevront des types améliorés dès la publication de nouvelles versions du runtime. Notre système utilise désormais le nouveau système runtime-type-information (RTTI) de workerd pour interroger les types d'API du runtime Workers, plutôt que d'essayer d'extraire ces informations depuis les AST C++ analysés.

// 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 = [ ... ], ...)

Nous transmettons ensuite ce RTTI à un programme TypeScript qui utilise l'API TypeScript Compiler pour générer des déclarations et exécuter des transformations AST afin de les mettre en ordre. Celui-ci est intégré au système de version Bazel de workerd, ce qui signifie que la génération de types relève maintenant d'une commande bazel build //types:types unique. Nous utilisons le cache de Bazel pour limiter autant que possible la reconstruction pendant la génération.

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>; }
new automatic type generation architecture

Si que les types générés automatiquement décrivent correctement l'interface JavaScript des API d'exécution de Workers, TypeScript fournit des fonctionnalités supplémentaires que nous pouvons utiliser pour accroître la fidélité des types et améliorer l'ergonomie pour les développeurs. Notre système nous permet d'écrire à la main des « substitutions » TypeScript partielles, qui sont fusionnées avec les types générés automatiquement. Ceci nous offre les possibilités suivantes :

  • Ajouter des paramètres de type (génériques) aux types tels que ReadableStream et éviter les valeurs de type any.
  • Spécifier la correspondance entre les types d'entrée et de sortie avec les surcharges de fonction. Par exemple, KVNamespace#get() devrait renvoyer une chaîne lorsque l'argument type est text, mais renvoyer ArrayBuffer lorsque l'argument type est arrayBuffer.
  • Renommer les types en fonction des normes TypeScript et réduire la verbosité.
  • Remplacer entièrement un type pour des déclarations plus précises. Par exemple, nous remplaçons WebSocketPair par une déclaration const pour améliorer les types avec Object.values().
  • Fournir des types pour les valeurs qui ne comportent pas de type en interne, à l'image de l'objet Request#cf.
  • Masquer les types internes non utilisables dans votre instance Workers.

Auparavant, ces remplacements étaient définis dans des fichiers TypeScript distincts des déclarations C++ qu'ils remplaçaient. Cela signifiait qu'ils étaient souvent désynchronisés par rapport aux déclarations d'origine. Dans le nouveau système, les remplacements sont définis parallèlement aux déclarations originales avec des macros C++ ; ils peuvent donc être examinés en même temps que les modifications de mise en œuvre du runtime. Reportez-vous au fichier README du code JavaScript glue de workerd pour beaucoup plus d'informations détaillées et d'exemples.

Essayez de coder avec workers-types dès aujourd'hui !

Nous vous encourageons à installer la version la plus récente de @cloudflare/workers-types avec la commande npm install --save-dev @cloudflare/workers-types@latest et d'essayer la nouvelle commande wrangler types. Nous publierons une nouvelle version des types avec chaque version de workerd. Faites-nous part de vos commentaires sur le canal Discord Cloudflare Developers et ouvrez un incident sur GitHub si vous découvrez des types que nous pourrions améliorer.

We protect entire corporate networks, help customers build Internet-scale applications efficiently, accelerate any website or Internet application, ward off DDoS attacks, keep hackers at bay, and can help you on your journey to Zero Trust.

Visit 1.1.1.1 from any device to get started with our free app that makes your Internet faster and safer.

To learn more about our mission to help build a better Internet, start here. If you're looking for a new career direction, check out our open positions.
Developer Week (FR)Developers (FR)Wrangler (FR)Français

Follow on X

Brendan Coll|@_mrbbot
Cloudflare|@cloudflare

Related posts

April 05, 2024 1:01 PM

Disponibilité générale de l'API Browser Rendering, déploiement de Cloudflare Snippets et mise à disposition de Workers for Platforms pour l'ensemble des utilisateurs

L'API Browser Rendering est désormais accessible à tous les clients d'une offre Workers payante avec gestion améliorée des sessions...

April 04, 2024 1:05 PM

Nouveaux outils pour la sécurité de la production : déploiements graduels, Stack Traces, contrôle du volume de requêtes et nouveaux SDK

Nous annonçons aujourd'hui cinq nouveautés pensées pour mettre davantage de puissance entre vos mains (déploiements graduels, traces d'appels mappées à la source dans Tail Workers, nouvelle API de contrôle du volume de requêtes, nouveaux SDK pour API et mises à jour de Durable Objects)...

April 03, 2024 1:30 PM

R2 ajoute les notifications d'événements, la prise en charge des migrations depuis Google Cloud Storage et un niveau de stockage pour accès occasionnel

Nous nous réjouissons d'annonce trois nouvelles fonctionnalités pour Cloudflare R2 : les notifications d'événements, la prise en charge des migrations depuis Google Cloud Storage et un niveau de stockage pour accès occasionnel...