Subscribe to receive notifications of new posts:

Melhorando a compatibilidade do Workers TypeScript: precisão, ergonomia e interoperabilidade

11/18/2022

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

O TypeScript torna mais fácil para os desenvolvedores escrever código que não falha, detectando erros de tipo antes que o programa seja executado. Queremos que os desenvolvedores aproveitem essas ferramentas, e é por isso que, há um ano, criamos um sistema para gerar automaticamente tipos TypeScript para o tempo de execução do Cloudflare Workers. Ele permitia que os desenvolvedores vissem as conclusões de código em seus IDEs para APIs do Workers e digitassem o código de verificação antes da implantação. A cada semana, uma nova versão dos tipos seria publicada, refletindo as mudanças mais recentes.

No ano passado, recebemos muitos comentários de clientes e equipes internas sobre como poderíamos melhorar nossos tipos. Com a mudança para o sistema de compilação Bazel em preparação para o código aberto do tempo de execução, vimos uma oportunidade de reconstruir nossos tipos para serem mais precisos, fáceis de usar e simples de gerar. Hoje, temos o prazer de anunciar o próximo grande lançamento do @cloudflare/workers-types com vários novos recursos e o código aberto dos scripts de geração automática totalmente reescritos.

Como usar o TypeScript com o Workers

Configurar o TypeScript no Workers é fácil. Se está começando com o Workers, instale o Node.js e execute npx wrangler init em seu terminal para gerar um novo projeto. Se tiver um projeto Workers existente e quiser aproveitar nossos tipos aprimorados, instale as versões mais recentes do TypeScript e @cloudflare/workers-types com npm install --save-dev typescript @cloudflare/workers-types@latest e crie um arquivo tsconfig.json com o seguinte conteúdo:

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

Seu editor agora vai destacar os problemas e fornecer conclusões de código enquanto você digita, levando a uma experiência de desenvolvedor menos propensa a erros e mais agradável.

Editor destacando o uso incorreto de set em vez de put e fornecendo conclusões de código

Interoperabilidade aprimorada com tipos padrão

O Cloudflare Workers implementa muitas das mesmas APIs de tempo de execução dos navegadores, e estamos trabalhando para melhorar ainda mais a conformidade de nossos padrões com o WinterCG. No entanto, sempre haverá diferenças fundamentais entre o que os navegadores e o Workers podem fazer. Por exemplo, os navegadores podem reproduzir arquivos de áudio, enquanto o Workers tem acesso direto à rede da Cloudflare para armazenar dados distribuídos globalmente. Essa incompatibilidade significa que as APIs de tempo de execução e os tipos fornecidos por cada plataforma são diferentes, o que dificulta o uso de tipos do Workers com estruturas, como o Remix, que executam os mesmos arquivos na rede da Cloudflare e no navegador. Esses arquivos precisam ser verificados quanto ao tipo lib.dom.d.ts, que é incompatível com nossos tipos.

Para resolver esse problema, agora geramos uma versão separada de nossos tipos que podem ser importados seletivamente, sem a necessidade de incluir @cloudflare/workers-types no campo de tipos do seu tsconfig.json. Aqui está um exemplo de como fica:

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

declare const USERS_NAMESPACE: KVNamespace;

Além disso, geramos automaticamente uma diferença de nossos tipos em relação a lib.webworker.d.ts do TypeScript. No futuro, vamos usar isso para identificar áreas em que podemos melhorar ainda mais nossa conformidade com as especificações.

Compatibilidade aprimorada com datas de compatibilidade

A Cloudflare preserva as promessas sólidas de compatibilidade com versões anteriores para todas as APIs que fornecemos. Usamos sinalizadores e datas de compatibilidade para fazer alterações importantes de maneira compatível com versões anteriores. Às vezes, esses sinalizadores de compatibilidade alteram os tipos. Por exemplo, o sinalizador global_navigator adiciona um novo navigator global e o sinalizador url_standard altera a assinatura do construtor URLSearchParams.

Agora permitimos que você selecione a versão dos tipos que correspondem à sua data de compatibilidade, para que você tenha certeza de que não está usando recursos que não serão compatíveis com o tempo de execução.

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

Integração aprimorada com o Wrangler

Além das datas de compatibilidade, a configuração do ambiente do Workers também afeta o tempo de execução e a superfície da API de tipo. Se você tiver ligações como namespaces KV ou buckets R2 configuradas em seu wrangler.toml, elas precisam ser refletidas nos tipos TypeScript. Da mesma forma, regras personalizadas de texto, dados e módulo WebAssembly precisam ser declaradas para que o TypeScript conheça os tipos de exportações. Anteriormente, cabia a você criar um arquivo TypeScript de ambiente separado contendo essas declarações.

Para manter o wrangler.toml como a única fonte de verdade, agora você pode executar npx wrangler types para gerar esse arquivo automaticamente.

Por exemplo, o seguinte wrangler.toml

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

…gera estes tipos de ambiente:

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

Documentação e logs de alterações integrados aprimorados

As conclusões de código fornecem uma ótima maneira para os desenvolvedores novos na plataforma Workers explorarem a superfície da API. Agora incluímos a documentação para APIs padrão dos tipos oficiais do TypeScript em nossos tipos. Também estamos iniciando o processo de incluir documentos para APIs específicas da Cloudflare neles.

docs in types shown with code completions

Para desenvolvedores que já usam a plataforma Workers, pode ser difícil ver como os tipos estão mudando a cada versão do @cloudflare/workers-types. Para evitar erros de tipo e destacar novos recursos, agora geramos um log de alterações detalhado com cada versão dividindo as definições novas, alteradas e removidas.

Como a geração de tipos funciona internamente?

Conforme mencionado antes, reconstruímos completamente os scripts de geração automática de tipos para serem mais confiáveis, extensíveis e fáceis de manter. Isso significa que os desenvolvedores obterão tipos aprimorados assim que novas versões do tempo de execução forem publicadas. Nosso sistema agora usa o novo sistema do Workers, runtime-type-information (RTTI) para consultar tipos de APIs de tempo de execução do Workers, em vez de tentar extrair essas informações de C++ ASTs analisados.

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

Em seguida, passamos esse RTTI para um programa TypeScript que usa TypeScript Compiler API para gerar declarações e realizar transformações AST para organizá-las. Isso é incorporado ao sistema de compilação Bazel do workerd, o que significa que a geração de tipos agora é um único comando bazel build //types:types de compilação bazel. Aproveitamos o cache do Bazel para recompilar o mínimo possível durante a geração.

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

Embora os tipos gerados automaticamente descrevam de forma correta a interface JavaScript das APIs de tempo de execução do Workers, o TypeScript fornece recursos adicionais que podemos usar para fornecer tipos de maior fidelidade e melhorar a ergonomia do desenvolvedor. Nosso sistema nos permite escrever manualmente “substituições” parciais do TypeScript que são mescladas com os tipos gerados automaticamente. Isso nos permite…

  • Adicionar parâmetros de tipo (genéricos) a tipos como ReadableStream e evitar valores de tipos any.
  • Especificar a correspondência entre os tipos de entrada e saída com sobrecargas de método. Por exemplo, KVNamespace#get() deve retornar uma string quando o argumento de type for text, mas ArrayBuffer quando for arrayBuffer.
  • Renomear os tipos para corresponder aos padrões do TypeScript e reduzir o excesso de palavras.
  • Substituir totalmente um tipo para obter declarações mais precisas. Por exemplo, substituímos WebSocketPair por uma declaração const para melhores tipos com Object.values().
  • Fornecer tipos para valores que não são digitados internamente, como o objeto Request#cf.
  • Ocultar tipos internos que não podem ser usados em seu Workers.

Anteriormente, essas substituições eram definidas em arquivos TypeScript separados para as declarações C++ que estavam substituindo. Isso significava que muitas vezes elas ficavam fora de sincronia com as declarações originais. No novo sistema, as substituições são definidas juntamente com os originais com macros C++, o que significa que podem ser revisadas junto com as alterações de implementação de tempo de execução. Consulte o README do workerd JavaScript glue code para muitos outros detalhes e exemplos.

Experimente os tipos com workers-types ainda hoje.

Recomendamos que você atualize para a versão mais recente de @cloudflare/workers-types com npm install --save-dev @cloudflare/workers-types@latest e experimente o novo comando wrangler types. Vamos publicar uma nova versão dos tipos com cada lançamento do workerd. Compartilhe sua opinião no Discord para desenvolvedores da Cloudflare e abra uma questão no GitHub caso encontre algum tipo que possa ser melhorado.

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 (PT)Developers (PT)Wrangler (PT)Português

Follow on X

Brendan Coll|@_mrbbot
Cloudflare|@cloudflare

Related posts

November 17, 2022 2:05 PM

Incremente seus sites no Cloudflare Pages com a disponibilidade geral do Pages Functions

o Pages é oficialmente uma plataforma full stack, com o Pages Functions agora em disponibilidade geral. O Pages está aproveitando o poder e a escalabilidade do Workers e especializando os recursos para que se alinhem à experiência do desenvolvedor do Pages...

November 17, 2022 2:00 PM

Adoção gradual de micro front-ends com o Cloudflare Workers

com o Cloudflare Workers, nossa arquitetura de micro front-ends baseada em fragmentos e nossa técnica de "fragment piercing", as equipes de engenharia podem aprimorar seus grandes front-ends gradualmente em uma fração do tempo que seria necessário...