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:
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.
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": ["esnext"],
"types": ["@cloudflare/workers-types"]
}
}
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:
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.
import type { KVNamespace } from "@cloudflare/workers-types";
declare const USERS_NAMESPACE: KVNamespace;
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.
Mejora de la integración con Wrangler
{
"compilerOptions": {
...
"types": ["@cloudflare/workers-types/2022-08-04"]
}
}
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
…
…genera estos tipos de ambiente:
kv_namespaces = [{ binding = "MY_NAMESPACE", id = "..." }]
rules = [{ type = "Text", globs = ["**/*.txt"] }]
Mejora de la documentación y registros de cambios integrados
interface Env {
MY_NAMESPACE: KVNamespace;
}
declare module "*.txt" {
const value: string;
export default value;
}
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.
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.
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.
// 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 = [ ... ], ...)
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:
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>; }
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 detype
estext
, peroArrayBuffer
cuando esarrayBuffer
.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ónconst
para mejorar los tipos conObject.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.