Subscribe to receive notifications of new posts:

Subscription confirmed. Thank you for subscribing!

自动生成 Cloudflare Workers 的类型

Loading...

根据过往情况来看,让 Rust 和 TypeScript 类型的存储库保持最新十分困难。它们是手动生成,这意味着面临不准确和过时的风险。直到最近,每当类型发生变化时,都需要手动更新 workers-types 存储库。我们还习惯于为大多数完整的浏览器 API 添加类型信息。这会导致混淆,当人们尝试使用不受 Workers 运行时支持的浏览器 API 时,它们会编译,但会抛出错误。

而在这个夏天,一切都发生了变化。我们的实习生 Brendan Coll 构建了一个可生成类型的自动化管道。每当我们构建 Workers 运行时时,它就会运行一次,为我们的 TypeScript 和 Rust 存储库生成类型。现在,所有内容都准确且为最新。

快速概览

每次构建 Workers 运行时代码时,都会有一个脚本在公共 API 上运行,生成 Rust 和 TypeScript 类型,以及一个包含静态类型中间表示的 JSON 文件。这些类型将被发送到适当的存储库,JSON 文件也会被上传,以防人们想要创建他们自己的类型包。这一点将在后面进行详述。

这意味着静态类型将始终准确且为最新。它还允许项目以其他静态类型语言运行 Workers,以从我们的中间表示生成他们自己的类型。下面是来自我们 Cloudflare 机器人的示例 PR。它在运行时类型中检测到一个变化,并正在更新 TypeScript 文件和中间表示。

使用自动生成的类型

首先,使用 wrangler 生成一个新的 TypeScript 项目:

$ wrangler generate my-typescript-worker https://github.com/cloudflare/worker-typescript-template


如果您已经拥有 TypeScript 项目,则可以使用以下命令安装最新版本的 workers-types

$ npm install --save-dev @cloudflare/workers-types

然后将 @cloudflare/workers-types 添加到项目的 tsconfig.json 文件。

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

之后,应当会在您选择的 IDE 中自动完成类型。

了解详情

下面是来自 Workers 运行时代码库的一些示例代码。

class Blob: public js::Object {
public:
  typedef kj::Array<kj::OneOf<kj::Array<const byte>, kj::String, js::Ref<Blob>>> Bits;
  struct Options {
    js::Optional<kj::String> type;
    JS_STRUCT(type);
  };

  static js::Ref<Blob> constructor(js::Optional<Bits> bits, js::Optional<Options> options);
  
  int getSize();
  js::Ref<Blob> slice(js::Optional<int> start, js::Optional<int> end);

  JS_RESOURCE_TYPE(Blob) {
    JS_READONLY_PROPERTY(size, getSize);
    JS_METHOD(slice);
  }
};

每次构建期间,都会有一个 Python 脚本在该代码上运行,并生成一个 Abstract Syntax Tree,其中包含有关函数的信息,包括标识符、任何参数类型和任何返回类型。

{
  "name": "Blob",
  "kind": "class",
  "members": [
    {
      "name": "size",
      "type": {
        "name": "integer"
      },
      "readonly": true
    },
    {
      "name": "slice",
      "type": {
        "params": [
          {
            "name": "start",
            "type": {
              "name": "integer",
              "optional": true
            }
          },
          {
            "name": "end",
            "type": {
              "name": "integer",
              "optional": true
            }
          }
        ],
        "returns": {
          "name": "Blob"
        }
      }
    }
  ]
}

最后,会向 TypeScript 类型存储库自动传送带有已更新类型的 PR。

declare type BlobBits = (ArrayBuffer | string | Blob)[];

interface BlobOptions {
  type?: string;
}

declare class Blob {
  constructor(bits?: BlobBits, options?: BlobOptions);
  readonly size: number;
  slice(start?: number, end?: number, type?: string): Blob;
}

覆盖

在某些情况下,TypeScript 支持一些 C++ 运行时不支持的概念,也就是泛型和函数重载。在这些情况下,我们会使用分部声明覆盖生成的类型。例如,DurableObjectStorage 为其 getter 和 setter 函数使用大量泛型。

declare abstract class DurableObjectStorage {
	 get<T = unknown>(key: string, options?: DurableObjectStorageOperationsGetOptions): Promise<T | undefined>;
	 get<T = unknown>(keys: string[], options?: DurableObjectStorageOperationsGetOptions): Promise<Map<string, T>>;
	 
	 list<T = unknown>(options?: DurableObjectStorageOperationsListOptions): Promise<Map<string, T>>;
	 
	 put<T>(key: string, value: T, options?: DurableObjectStorageOperationsPutOptions): Promise<void>;
	 put<T>(entries: Record<string, T>, options?: DurableObjectStorageOperationsPutOptions): Promise<void>;
	 
	 delete(key: string, options?: DurableObjectStorageOperationsPutOptions): Promise<boolean>;
	 delete(keys: string[], options?: DurableObjectStorageOperationsPutOptions): Promise<number>;
	 
	 transaction<T>(closure: (txn: DurableObjectTransaction) => Promise<T>): Promise<T>;
	}

您还可以使用 Markdown 编写类型覆盖。这里是覆盖 KVNamespace 类型的一个示例。

创建您自己的类型

JSON IR(中间表示)已经和 TypeScript 类型一起开放源码,可在本 GitHub 存储库中找到。我们还开放了类型架构本身的源码,该类型架构描述了 IR 的格式。如果您有兴趣为您自己的语言生成 Workers 类型,则可以使用 IR,它采用“规范化的”数据结构描述声明,并从中生成类型。

“workers.json”内的声明包含用于派生函数签名的元素和代码生成所需的其他元素,例如标识符、参数类型、返回类型和错误管理。一个具体用例是为编译到 WebAssembly 的语言生成外部函数声明,以精确地从 Workers 运行时导入可用的函数调用集。

总结

Cloudflare 深切关注支持 TypeScript 和 Rust 生态系统。Brendan 制作了一个工具,可确保两种语言的类型信息都始终为最新且准确。我们还以 JSON 格式开源了类型信息本身,这样,感兴趣的任何人都可以为他们想要的任何语言创建类型数据!

我们保护 整个企业网络, 帮助客户高效构建 互联网规模应用, 加速一切 网站或互联网应用 , 抵御 DDoS 攻击, 阻止 黑客, 并可帮助您踏上 Zero Trust 之旅

从任何设备访问 1.1.1.1, 使用我们的免费应用加速和保护您的互联网。

如需进一步了解我们帮助构建更美好互联网的使命,请从 这里 开始。如果您正在寻找新的职业方向,请查看 我们的空缺职位

Cloudflare Workers (CN) 简体中文 (CN) 无服务器 Full Stack Week (CN)

Follow on Twitter

Brendan Coll |@_mrbbot
Jonathan Kuperman |@jkup
Cloudflare |Cloudflare

Related Posts