Skip to content

Workers TypeScript -- Type-Safe Development

DodaTech 6 min read

In this tutorial, you'll learn about Workers TypeScript. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Cloudflare Workers TypeScript support enables developers to write type-safe serverless functions with full compile-time error checking, autocompletion, and better Refactoring capabilities compared to plain JavaScript Workers.

Why TypeScript for Workers

JavaScript Workers are flexible but offer no Type Checking. A simple typo like env.MY_KV.get instead of env.MY_KV.get silently returns undefined at runtime. TypeScript catches these errors during development, long before deployment. The Workers runtime includes official type definitions for all bindings -- KV, D1, R2, Queues, Durable Objects, AI, and more -- giving you autocompletion for every method and its parameters. As your Worker project grows beyond a few hundred lines, TypeScript becomes essential for maintaining code quality. Unlike Cloudflare's JavaScript-only examples which are concise but fragile, TypeScript Workers scale to enterprise complexity. Combined with TypeScript's strict mode, you can enforce immutability, null checking, and exhaustive switch statements across your entire codebase.

Real-world use: An API Gateway Worker with 15 route handlers, each calling different combinations of KV, D1, and R2. TypeScript ensures that every handler receives correctly typed bindings, and renaming a binding in wrangler.toml immediately surfaces every usage that needs updating.

TypeScript Architecture

flowchart LR
    TS[.ts Source] --> TSC[TypeScript Compiler]
    TSC --> JS[.js Output]
    JS --> W[Wrangler Deploy]
    W --> R[Workers Runtime]
    D[Type Definitions] --> TSC
    D --> E[Editor Autocompletion]

    subgraph Dev_Experience
        L[Linting] --> TS
        F[Formatting] --> TS
        T[Testing] --> TS
    end

    style TS fill:#3498db,color:#fff
    style D fill:#f90,color:#fff
    style TSC fill:#2ecc71,color:#fff

Wrangler automatically compiles TypeScript to JavaScript during deployment. The type definitions from @<a href="/web-servers-hosting/cloudflare/">cloudflare</a>/workers-types provide accurate types for all runtime APIs, bindings, and environment variables.

Setting Up a TypeScript Worker

# Create a new TypeScript Worker
npx wrangler init my-typescript-worker --yes
# Select "Yes" when asked about TypeScript

# Or add TypeScript to an existing Worker:
npm install --save-dev typescript @cloudflare/workers-types
npx tsc --init --module es2022 --moduleResolution bundler --outDir dist

# Expected output:
# ✔ Created wrangler.toml
# ✔ Created src/index.ts
# ✔ Created tsconfig.json
# ✔ Installed @cloudflare/workers-types
// src/index.ts
import { D1Database, R2Bucket } from '@cloudflare/workers-types';

export interface Env {
  DB: D1Database;
  ASSETS: R2Bucket;
  API_KEY: string;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    const { results } = await env.DB.prepare(
      'SELECT * FROM users LIMIT 10'
    ).all();

    return new Response(JSON.stringify(results), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};

// TypeScript Error if you mis-type:
// env.DB.prepar('SELECT *') -> Property 'prepar' does not exist on type 'D1Database'
// env.API_KEY -> Type 'string' is correctly inferred as non-nullable

The Env interface defines all bindings with their proper types. The @<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>/workers-types package provides D1Database, R2Bucket, ExecutionContext, KVNamespace, and dozens of other types. Wrangler also supports generating the Env type from your wrangler.toml.

Using Auto-Generated Binding Types

// Generate types from wrangler.toml:
// wrangler types

// This creates a worker-configuration.d.ts file automatically

// wrangler.toml
// name = "typed-worker"
// [[kv_namespaces]]
// binding = "CACHE"
// id = "abc123"
// [[d1_databases]]
// binding = "DB"
// database_id = "db-456"
// database_name = "my-db"

// Generated: worker-configuration.d.ts
interface Env {
  CACHE: KVNamespace;
  DB: D1Database;
}

// src/index.ts -- no manual Env declaration needed
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // env.CACHE has full KVNamespace autocompletion
    const cached = await env.CACHE.get('homepage');
    if (cached) {
      return new Response(cached);
    }

    const content = await fetch('https://origin.example.com');
    const text = await content.text();
    await env.CACHE.put('homepage', text, { expirationTtl: 300 });

    return new Response(text);
  }
};

// Expected output: compiles without errors
// If wrangler.toml changes, re-run "wrangler types" to regenerate

Running wrangler types auto-generates the Env interface from your wrangler.toml configuration. This eliminates manual type declarations and ensures your types stay in sync with your bindings. Regenerate whenever you add or rename bindings.

Type-Safe Durable Object

import { DurableObject } from 'cloudflare:workers';

interface CounterState {
  value: number;
  lastUpdated: number;
}

interface CounterMethods {
  increment(amount: number): number;
  getValue(): CounterState;
  reset(): void;
}

export class TypedCounter extends DurableObject implements CounterMethods {
  private state: CounterState;

  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    this.state = { value: 0, lastUpdated: Date.now() };
  }

  async increment(amount: number): Promise<number> {
    // TypeScript ensures `amount` is a number, not a string
    this.state.value += amount;
    this.state.lastUpdated = Date.now();
    await this.ctx.storage.put('state', this.state);
    return this.state.value;
  }

  async getValue(): Promise<CounterState> {
    const stored = await this.ctx.storage.get<CounterState>('state');
    return stored || this.state;
  }

  async reset(): Promise<void> {
    this.state = { value: 0, lastUpdated: Date.now() };
    await this.ctx.storage.put('state', this.state);
  }
}

// Expected compile-time errors:
// increment('5') -> Argument of type 'string' is not assignable to parameter of type 'number'
// getValue().valu -> Property 'valu' does not exist on type 'CounterState'

Defining explicit interfaces for your Durable Object's methods and state lets TypeScript catch entire categories of bugs at compile time. The generic storage.get<T>() method infers the return type from the interface, eliminating the need for runtime type checks.

Common Errors

Error Cause Fix
Cannot find name 'KVNamespace' Missing @<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>/workers-types dependency Install the package: npm install --save-dev @<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>/workers-types
Type 'Request' is not generic Using older TypeScript or incorrect lib setting Ensure tsconfig.json has "types": ["@<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>/workers-types"]
Property 'waitUntil' does not exist on type 'ExecutionContext' Incorrect import for ExecutionContext Import from @<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>/workers-types not from the standard TypeScript lib
Cannot find module '<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>:workers' Durable Object import not resolved Add "moduleResolution": "bundler" to tsconfig.json
Type 'string' is not assignable to type 'number' Binding type mismatch with wrangler.toml Run wrangler types to regenerate the Env interface

Practice Questions

  1. What command generates the Env type definition from wrangler.toml?
  2. Which package provides TypeScript types for Workers runtime APIs?
  3. How does TypeScript improve Durable Object development beyond plain JavaScript?

FAQ

{{< faq "Do I need a build step for TypeScript Workers?">}} No. Wrangler handles TypeScript compilation automatically during wrangler deploy and wrangler dev. You just write .ts files and Wrangler compiles them to JavaScript using esbuild internally. No manual tsc step is required for deployment. {{< /faq >}}

{{< faq "Can I use TypeScript with Workers Pages Functions?">}} Yes. Pages Functions support TypeScript natively. Files in the functions/ directory can use .ts extensions, and Type Checking is available through the same @<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>/workers-types package. Pages Functions also support auto-generated types from project configuration. {{< /faq >}}

What tsconfig settings are recommended for Workers?

Use "module": "es2022", "moduleResolution": "bundler", "target": "es2022", and add "@<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>/workers-types" to the "types" array. Set "strict": true for maximum type safety. These settings ensure compatibility with the Workers runtime's module system.

Summary

TypeScript brings type safety, autocompletion, and better tooling to Cloudflare Workers development. Use @<a href="/web-servers-hosting/cloudflare/">Cloudflare</a>/workers-types for runtime API types, auto-generate binding types with wrangler types, and define explicit interfaces for Durable Objects. TypeScript catches binding name typos, wrong argument types, and missing properties at compile time, reducing runtime failures in production. Wrangler handles compilation automatically so there is no separate build step. DodaTech uses TypeScript for all production Workers to enforce code quality across its engineering team.

This guide is brought to you by the developers of Cloudflare, TypeScript, and Durga Antivirus Pro at DodaTech.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro