Suscríbete para recibir notificaciones de nuevas publicaciones:

Mejoramos la compatibilidad de Workers TypeScript: precisión, ergonomía e interoperabilidad

18/11/2022

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

TypeScript permite a los desarrolladores escribir código sin errores gracias a la detección de errores de escritura antes de que ejecutes un programa. Queremos que los desarrolladores aprovechen esta herramienta, por eso hace un año creamos un sistema para generar automáticamente tipos TypeScript para el entorno de ejecución de Cloudflare Workers. Esto permitió a los desarrolladores completar el código en sus IDE para las API de Workers y escribir el código de verificación antes de su implementación. Cada semana, se publicará una nueva versión de los tipos con el fin de reflejar los cambios más recientes.

A lo largo del año pasado, recibimos muchos comentarios de clientes y equipos internos sobre cómo podíamos mejorar nuestros tipos. El cambio al sistema de compilación Bazel en preparación para el código abierto del entorno de ejecución, nos planteó la oportunidad de recopilar nuestros tipos para que fueran más precisos, más fáciles de usar y más simples de generar. Hoy, nos complace anunciar el próximo lanzamiento importante de @cloudflare/workers-types con numerosas funciones nuevas y el código abierto de los scripts de generación automática completamente reescritos.

Cómo usar TypeScript con Workers

Configurar TypeScript en Workers es fácil. Si te estás iniciando en Workers, instala Node.js. A continuación, ejecuta npx wrangler init en tu terminal para generar un nuevo proyecto. Si tienes un proyecto de Workers y deseas aprovechar nuestras escrituras mejoradas, instala las últimas versiones de TypeScript y @cloudflare/workers-types con npm install --save-dev typescript @cloudflare/workers-types@latest. A continuación, crea un archivo tsconfig.json con el siguiente contenido:

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

Tu editor ahora identificará los problemas y te proporcionará códigos completos conforme escribes, lo que promoverá experiencias más positivas y menos propensas a errores a los desarrolladores.

El editor identifica el uso incorrecto de set en lugar de put, y ofrece códigos completados

Mejora de la interoperabilidad con tipos estándar

Cloudflare Workers implementa muchas de las mismas API del entorno de ejecución que los navegadores y estamos trabajando para seguir mejorando el cumplimiento de nuestros estándares con WinterCG. Sin embargo, siempre habrá diferencias fundamentales entre lo que pueden hacer los navegadores y Workers. Por ejemplo, los navegadores pueden reproducir archivos de audio, mientras que Workers tiene acceso directo a la red de Cloudflare para almacenar datos distribuidos a nivel mundial. Este desajuste significa que las API del entorno de ejecución y los tipos proporcionados por cada plataforma son diferentes, lo que a su vez dificulta el uso de los tipos de Workers con marcos, como Remix, que ejecutan los mismos archivos en la red de Cloudflare y en el navegador. Es necesario verificar el tipo de estos archivos con lib.dom.d.ts, que es incompatible con nuestros tipos.

Para resolver este problema, hemos generado una versión independiente de nuestros tipos que se pueden importar de forma selectiva, sin tener que incluir @cloudflare/workers-types en el campo types de tu tsconfig.json. A continuación, te mostramos un ejemplo:

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

declare const USERS_NAMESPACE: KVNamespace;

Además, generamos automáticamente un comando diff de nuestros tipos con lib.webworker.d.ts de TypeScript. De ahora en adelante, lo usaremos para identificar áreas en las que podemos seguir mejorando nuestra conformidad con las especificaciones.

Mejora de la compatibilidad con fechas de compatibilidad

Cloudflare mantiene un elevado compromiso de compatibilidad con versiones anteriores para todas las API que proporcionamos. Utilizamos indicadores y fechas de compatibilidad para que los cambios que realizamos sean compatibles con versiones anteriores. A veces, estos indicadores de compatibilidad cambian los tipos. Por ejemplo, el indicador global_navigator añade un nuevo navigator global, y el indicador url_standard cambia la firma del constructor URLSearchParams.

Ahora puedes seleccionar la versión de los tipos que coincida con tu fecha de compatibilidad para garantizar que no estás utilizando funciones que no serán compatibles en el entorno de ejecución.

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

Mejora de la integración con Wrangler

Además de las fechas de compatibilidad, la configuración de tu entorno Worker también afecta al entorno de ejecución y a la superficie de la API de tipo. Si has configurado enlaces como KV namespaces o R2 buckets en tu wrangler.toml, estos se deben reflejar en los tipos TypeScript. De igual forma, se debe indicar el texto personalizado, los datos y las reglas del módulo WebAssembly para que TypeScript conozca los tipos para exportar. Anteriormente, dependía de ti crear un archivo TypeScript ambiental independiente que incluyera estas declaraciones.

Para mantener wrangler.toml como la única fuente fiable, puedes ejecutar los npx wrangler types para generar este archivo automáticamente.

Por ejemplo, el siguiente wrangler.toml

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

…genera estos tipos de ambiente:

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

Mejora de la documentación y registros de cambios integrados

Los códigos completados proporcionan una excelente manera para que los desarrolladores que acaban de iniciarse en la plataforma Workers exploren la superficie de la API. Ahora incluimos la documentación para las API estándar de los tipos oficiales de TypeScript en nuestros tipos. También estamos comenzando el proceso para incorporar documentos para API específicas de Cloudflare en estos.

docs in types shown with code completions

A los desarrolladores que ya usan la plataforma Workers les puede resultar difícil ver cómo cambian los tipos con cada versión de @cloudflare/workers-types. Para evitar errores tipográficos e identificar nuevas funciones, ahora generamos un registro de cambios detallado con cada versión que divide las definiciones nuevas, modificadas y eliminadas.

¿Cómo funciona la generación de tipos a nivel interno?

Como se mencionó anteriormente, hemos reconstruido por completo los scripts de generación automática de tipos para que sean más fiables, extensibles y fáciles de mantener. Esto significa que los desarrolladores obtendrán tipos mejorados tan pronto como se publiquen nuevas versiones del entorno de ejecución. Nuestro sistema ahora utiliza el nuevo sistema workerd de runtime-type-information (RTTI) para consultar tipos de las API del entorno de ejecución de Workers, en lugar de intentar extraer esta información de los C++ AST analizados.

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

A continuación, pasamos este RTTI a un programa TypeScript que usa el API del compilador TypeScript para generar declaraciones y realizar transformaciones AST para ordenarlas. Esto está integrado en el sistema de compilación Bazel de workerd, lo que significa que generar tipos ahora es un comando único bazel build //types:types. Usamos la caché de Bazel para recopilar lo menos posible durante la generación.

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 bien los tipos generados automáticamente describen correctamente la interfaz JavaScript de las API del entorno de ejecución de Workers, TypeScript proporciona funciones adicionales que podemos utilizar para proporcionar tipos más fieles y mejorar la ergonomía del desarrollador. Nuestro sistema nos permite escribir a mano "anulaciones" parciales de TypeScript que se fusionan con los tipos generados automáticamente. Esto nos permite:

  • Añadir parámetros de tipo (genéricos) a tipos como ReadableStream y evitar cualquier valor de tipos.
  • Especificar la correspondencia entre los tipos de entrada y salida con sobrecargas de métodos. Por ejemplo, KVNamespace#get() debe devolver una cadena cuando el argumento de type es text, pero ArrayBuffer cuando es arrayBuffer.
  • Cambiar el nombre de los tipos para que coincidan con los estándares de TypeScript y reduzca el nivel de detalle.
  • Reemplazar por completo un tipo para obtener declaraciones más precisas. Por ejemplo, reemplazamos WebSocketPair con una declaración const para mejorar los tipos con Object.values().
  • Proporcionar tipos para valores internos sin tipo, como el objeto Request#cf.
  • Ocultar los tipos internos que no se pueden usar en tu workers.

Anteriormente, estas anulaciones se definían en archivos TypeScript independientes de las declaraciones de C++ que invalidaban. Esto significaba que a menudo no estaban sincronizadas con las declaraciones originales. En el nuevo sistema, las anulaciones se definen junto con los originales con macros de C++, lo que significa que se pueden revisar junto con los cambios de implementación en el entorno de ejecución. Consulta el archivo README del código de adherencia JavaScript de workerd para ver más detalles y ejemplos.

¡Prueba a escribir con los tipos de Workers hoy mismo!

Te recomendamos que instales la nueva versión de @cloudflare/workers-types con npm install --save-dev @cloudflare/workers-types@latest y pruebes el nuevo comando wrangler types. Publicaremos una nueva versión de los tipos con cada lanzamiento de workerd. Dinos que piensas sobre el servidor Discord de Cloudflare Developers, y abre una incidencia en GitHub si ves tipos que podríamos mejorar.

Protegemos redes corporativas completas, ayudamos a los clientes a desarrollar aplicaciones web de forma eficiente , aceleramos cualquier sitio o aplicación web, prevenimos contra los ataques DDoS , mantenemos a raya a los hackers, y podemos ayudarte en tu recorrido hacia la seguridad Zero Trust.

Visita 1.1.1.1 desde cualquier dispositivo para empezar a utilizar nuestra aplicación gratuita y beneficiarte de una navegación más rápida y segura.

Para saber más sobre nuestra misión de ayudar a mejorar Internet, empieza aquí . Si estás buscando un nuevo rumbo profesional, consulta nuestras ofertas de empleo.

Developer Week (ES)Developers (ES)Wrangler (ES)Español

Síguenos en X

Brendan Coll|@_mrbbot
Cloudflare|@cloudflare

Publicaciones relacionadas