订阅以接收新文章的通知:

自动生成 Cloudflare Workers 的类型

2021-11-16

2 分钟阅读时间
这篇博文也有 English日本語版本。

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

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

快速概览

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

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

使用自动生成的类型

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

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

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

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

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

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

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

了解详情

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

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

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);
  }
};

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

{
  "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"
        }
      }
    }
  ]
}

覆盖

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 函数使用大量泛型。

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

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>;
	}

创建您自己的类型

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

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

总结

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

我们保护整个企业网络,帮助客户高效构建互联网规模的应用程序,加速任何网站或互联网应用程序抵御 DDoS 攻击,防止黑客入侵,并能协助您实现 Zero Trust 的过程

从任何设备访问 1.1.1.1,以开始使用我们的免费应用程序,帮助您更快、更安全地访问互联网。要进一步了解我们帮助构建更美好互联网的使命,请从这里开始。如果您正在寻找新的职业方向,请查看我们的空缺职位
Full Stack WeekCloudflare Workers开发人员ServerlessDeveloper Platform

在 X 上关注

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

相关帖子