Cloudflare Workersプロジェクトで、R2ストレージを扱う関数を実装していた。 TypeScriptとしては正常にコンパイルできるが、ESLintが`R2Object`型を認識せず、`no-undef`エラーが発生した。 正直なところ、このエラーの原因を特定するのに少し時間がかかった。 TypeScriptの型システムは完璧に動作しているのに、ESLintだけがエラーを出す——この状況は多くの開発者が直面する問題です。 ## こんな人におすすめ この記事は以下のような方に役立ちます。 - Cloudflare Workers + R2 で開発をしている - ESLintの`no-undef`エラーに遭遇して困っている - TypeScriptのグローバル型とESLintの違いを理解したい - 型安全性を保ちながらLintエラーを回避したい ## 目次 - [背景](#背景) - [こんな人におすすめ](#こんな人におすすめ) - [症状](#症状) - [原因](#原因) - [ESLintとTypeScriptの仕組みの違い](#eslintとtypescriptの仕組みの違い) - [なぜこの問題が発生するのか](#なぜこの問題が発生するのか) - [直し方](#直し方) - [解決策1: インターフェースを明示的に定義する(推奨)](#解決策1-インターフェースを明示的に定義する推奨) - [解決策2: ESLintのno-undefルールを無効化する](#解決策2-eslintのno-undefルールを無効化する) - [解決策3: globals設定で宣言する](#解決策3-globals設定で宣言する) - [学び](#学び) - [よくある質問(FAQ)](#よくある質問faq) - [まとめ](#まとめ) - [確認手順](#確認手順) - [参考リンク](#参考リンク) ## 症状 ``` /functions/api/storage/images/list.ts 214:42 error 'R2Object' is not defined no-undef ``` `@cloudflare/workers-types`のtriple-slash directiveを追加しても解決しなかった。 ```typescript /// <reference types="@cloudflare/workers-types" /> ``` 個人的に、この対処法が効かないときは「どうすればいいんだ?」と少し焦りました。 ## 原因 ### ESLintとTypeScriptの仕組みの違い ESLintはTypeScriptのtriple-slash directiveで定義されたグローバル型を認識しません。 TypeScriptコンパイラは認識するが、ESLintの`no-undef`ルールは別の仕組みで動作するのです。 ### なぜこの問題が発生するのか TypeScriptのtriple-slash directive(`/// <reference types="..." />`)は、TypeScriptコンパイラ専用の機能です。 これはコンパイル時にのみ参照され、ESLintのようなリンティングツールからは見えません。 ESLintの`no-undef`ルールは、[ESLintのドキュメント](https://eslint.org/docs/latest/rules/no-undef)にある通り、グローバル変数の定義を`globals`設定または`exported`コメントで認識します。 TypeScriptの型定義ファイル(`.d.ts`)で宣言されたグローバル型は、ESLintからは見えないのです。 詳細は、[`@typescript-eslint/parser`のドキュメント](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser)で確認できます。 ## 直し方 どの解決策を選ぶべきか、以下のフローチャートで判断できます。 ```mermaid flowchart TD Start[ESLintのno-undefエラーが発生] --> Q1{型安全性を維持したい?} Q1 -->|はい| A1[解決策1: インターフェース定義] Q1 -->|いいえ| Q2{型チェックが不要でOK?} Q2 -->|はい| A2[解決策2: no-undef無効化] Q2 -->|いいえ| A3[解決策3: globals設定] A1 --> R1[✅ 推奨: 型安全性を保てる] A2 --> R2[⚠️ 注意: 型チェックが緩くなる] A3 --> R3[⚠️ 注意: 型情報が失われる] style A1 fill:#c8e6c9 style A2 fill:#fff9c4 style A3 fill:#fff9c4 style R1 fill:#c8e6c9 style R2 fill:#ffe0b2 style R3 fill:#ffe0b2 ``` ### 解決策1: インターフェースを明示的に定義する(推奨) 必要なプロパティのみを持つインターフェースを明示的に定義します。 ```typescript /** * R2オブジェクトの必要なプロパティのみを定義 * (ESLint対応: @cloudflare/workers-typesのグローバル型はESLintに認識されないため) */ interface R2ObjectLike { key: string; uploaded: Date; size: number; } export function parseImageObject(object: R2ObjectLike, r2PublicUrl: string): ImageInfo | null { // ... } ``` 実際の`R2Object`型と互換性があるため、`env.STORAGE.list()`の結果をそのまま渡せます。 以下の図は、R2から取得したデータがどうやって関数に渡されるかを示したフローです。 ```mermaid sequenceDiagram participant R2 as R2ストレージ participant Env as env.STORAGE participant Func as parseImageObject関数 participant User as ユーザーコード R2->>Env: list()でR2Objectsを取得 Env->>Func: R2Object[]を渡す Note over Func: interface R2ObjectLike<br/>で型チェック✅ Func->>User: ImageInfo[]を返す Note over User: ESLintエラーなし✅ ``` この方法で型安全性も保ちながら、ESLintエラーを回避できました。 ### 解決策2: ESLintのno-undefルールを無効化する もう一つの方法は、`@typescript-eslint/no-undef`を使用するか、`no-undef`ルールを無効化することです。 ただし、この方法は型チェックが緩くなる可能性があるため推奨しません。 ```typescript /* eslint-disable no-undef */ // R2Objectを使用するコード /* eslint-enable no-undef */ ``` ### 解決策3: globals設定で宣言する `.eslintrc.json`で`globals`設定にR2Objectを追加する方法もあります。 ```json { "globals": { "R2Object": "readonly" } } ``` しかし、この方法では型情報が失われるため、解決策1のインターフェース定義が最も安全です。 ## 学び - ESLintとTypeScriptは型認識の仕組みが異なる - グローバル型に依存する場合、ESLint用に明示的な型定義が必要なことがある - 必要なプロパティのみを持つインターフェースを定義することで、型安全性を保ちながらESLintエラーを回避できる ## よくある質問(FAQ) ### Q. ESLintとTypeScriptのどちらを優先すべきですか? A. **両方のエラーを解消するのが理想**です。 TypeScriptの型チェックは実行時エラーを防ぐために重要です。 一方で、ESLintはコードの一貫性を保つために役立ちます。 インターフェース定義を使えば、両方の要件を満たせます。 ### Q. 他のCloudflare Workers型でも同様の問題が発生しますか? A. **はい、同様の問題が発生する可能性があります**。 `R2Bucket`、`Env`、`ExecutionContext`など、`@cloudflare/workers-types`で定義されるグローバル型すべてで同様の問題が発生します。 同じアプローチでインターフェースを定義することで解決できます。 ### Q. インターフェース定義が面倒です。もっと簡単な方法はありませんか? A. **`no-undef`ルールを無効化する方法**がありますが、推奨しません。 型チェックの緩和はバグの原因になる可能性があるため、最初からインターフェースを定義することをおすすめします。 ## まとめ 今回の問題から得た教訓をまとめます。 1. **ESLintとTypeScriptは別物** - ESLintはTypeScriptのtriple-slash directiveを認識しません 2. **明示的な型定義で解決** - 必要なプロパティのみを持つインターフェースを定義することで、両方の要件を満たせます 3. **実用的なアプローチ** - 実際の型と互換性があるため、既存のコードにそのまま適用できます 4. **同じ問題が他の型でも発生** - Cloudflare Workersのグローバル型すべてで同様の問題が発生する可能性があります この方法を採用したことで、Lintエラーが解消され、開発体験が改善されました。 ## 確認手順 1. `npm run lint` でエラーがないことを確認 2. `npm run test` でテストが通ることを確認 3. TypeScriptコンパイルエラーがないことを確認 ## 参考リンク - [ESLint no-undefルールのドキュメント](https://eslint.org/docs/latest/rules/no-undef) - [@typescript-eslint/parserのドキュメント](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser) --- <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "ESLintがCloudflare Workers型を認識しない問題の解決", "description": "Cloudflare WorkersでR2を使用する際、ESLintがR2Object型を認識しないno-undefエラーの原因と解決方法を解説。", "image": "https://wakatchi.dev/wp-content/uploads/2026/01/eslint-cloudflare-workers.png", "datePublished": "2026-01-01", "dateModified": "2026-01-01", "author": { "@type": "Person", "name": "wakatchi" }, "publisher": { "@type": "Organization", "name": "wakatchi.dev", "logo": { "@type": "ImageObject", "url": "https://wakatchi.dev/wp-content/uploads/logo.png" } }, "keywords": "ESLint, TypeScript, Cloudflare Workers, R2, 型定義, no-undef", "articleSection": "Infrastructure", "proficiencyLevel": "Beginner", "dependencies": "ESLint, TypeScript, @cloudflare/workers-types", "operatingSystem": "Any", "programmingLanguage": "TypeScript" } </script>

Cloudflare Workersプロジェクトで、R2ストレージを扱う関数を実装していた。
TypeScriptとしては正常にコンパイルできるが、ESLintがR2Object型を認識せず、no-undefエラーが発生した。

正直なところ、このエラーの原因を特定するのに少し時間がかかった。

TypeScriptの型システムは完璧に動作しているのに、ESLintだけがエラーを出す——この状況は多くの開発者が直面する問題です。

こんな人におすすめ

この記事は以下のような方に役立ちます。

  • Cloudflare Workers + R2 で開発をしている
  • ESLintのno-undefエラーに遭遇して困っている
  • TypeScriptのグローバル型とESLintの違いを理解したい
  • 型安全性を保ちながらLintエラーを回避したい

目次

症状

/functions/api/storage/images/list.ts
  214:42  error  'R2Object' is not defined  no-undef

@cloudflare/workers-typesのtriple-slash directiveを追加しても解決しなかった。

/// <reference types="@cloudflare/workers-types" />

個人的に、この対処法が効かないときは「どうすればいいんだ?」と少し焦りました。

原因

ESLintとTypeScriptの仕組みの違い

ESLintはTypeScriptのtriple-slash directiveで定義されたグローバル型を認識しません。
TypeScriptコンパイラは認識するが、ESLintのno-undefルールは別の仕組みで動作するのです。

なぜこの問題が発生するのか

TypeScriptのtriple-slash directive(/// <reference types="..." />)は、TypeScriptコンパイラ専用の機能です。
これはコンパイル時にのみ参照され、ESLintのようなリンティングツールからは見えません。

ESLintのno-undefルールは、ESLintのドキュメントにある通り、グローバル変数の定義をglobals設定またはexportedコメントで認識します。
TypeScriptの型定義ファイル(.d.ts)で宣言されたグローバル型は、ESLintからは見えないのです。

詳細は、@typescript-eslint/parserのドキュメントで確認できます。

直し方

どの解決策を選ぶべきか、以下のフローチャートで判断できます。

flowchart TD
    Start[ESLintのno-undefエラーが発生] --> Q1{型安全性を維持したい?}

    Q1 -->|はい| A1[解決策1: インターフェース定義]
    Q1 -->|いいえ| Q2{型チェックが不要でOK?}

    Q2 -->|はい| A2[解決策2: no-undef無効化]
    Q2 -->|いいえ| A3[解決策3: globals設定]

    A1 --> R1[✅ 推奨: 型安全性を保てる]
    A2 --> R2[⚠️ 注意: 型チェックが緩くなる]
    A3 --> R3[⚠️ 注意: 型情報が失われる]

    style A1 fill:#c8e6c9
    style A2 fill:#fff9c4
    style A3 fill:#fff9c4
    style R1 fill:#c8e6c9
    style R2 fill:#ffe0b2
    style R3 fill:#ffe0b2

解決策1: インターフェースを明示的に定義する(推奨)

必要なプロパティのみを持つインターフェースを明示的に定義します。

/**
 * R2オブジェクトの必要なプロパティのみを定義
 * (ESLint対応: @cloudflare/workers-typesのグローバル型はESLintに認識されないため)
 */
interface R2ObjectLike {
  key: string;
  uploaded: Date;
  size: number;
}

export function parseImageObject(object: R2ObjectLike, r2PublicUrl: string): ImageInfo | null {
  // ...
}

実際のR2Object型と互換性があるため、env.STORAGE.list()の結果をそのまま渡せます。

以下の図は、R2から取得したデータがどうやって関数に渡されるかを示したフローです。

sequenceDiagram
    participant R2 as R2ストレージ
    participant Env as env.STORAGE
    participant Func as parseImageObject関数
    participant User as ユーザーコード

    R2->>Env: list()でR2Objectsを取得
    Env->>Func: R2Object[]を渡す
    Note over Func: interface R2ObjectLike<br/>で型チェック✅
    Func->>User: ImageInfo[]を返す
    Note over User: ESLintエラーなし✅

この方法で型安全性も保ちながら、ESLintエラーを回避できました。

解決策2: ESLintのno-undefルールを無効化する

もう一つの方法は、@typescript-eslint/no-undefを使用するか、no-undefルールを無効化することです。
ただし、この方法は型チェックが緩くなる可能性があるため推奨しません。

/* eslint-disable no-undef */
// R2Objectを使用するコード
/* eslint-enable no-undef */

解決策3: globals設定で宣言する

.eslintrc.jsonglobals設定にR2Objectを追加する方法もあります。

{
  "globals": {
    "R2Object": "readonly"
  }
}

しかし、この方法では型情報が失われるため、解決策1のインターフェース定義が最も安全です。

学び

  • ESLintとTypeScriptは型認識の仕組みが異なる
  • グローバル型に依存する場合、ESLint用に明示的な型定義が必要なことがある
  • 必要なプロパティのみを持つインターフェースを定義することで、型安全性を保ちながらESLintエラーを回避できる

よくある質問(FAQ)

Q. ESLintとTypeScriptのどちらを優先すべきですか?

A. 両方のエラーを解消するのが理想です。
TypeScriptの型チェックは実行時エラーを防ぐために重要です。
一方で、ESLintはコードの一貫性を保つために役立ちます。
インターフェース定義を使えば、両方の要件を満たせます。

Q. 他のCloudflare Workers型でも同様の問題が発生しますか?

A. はい、同様の問題が発生する可能性があります
R2BucketEnvExecutionContextなど、@cloudflare/workers-typesで定義されるグローバル型すべてで同様の問題が発生します。
同じアプローチでインターフェースを定義することで解決できます。

Q. インターフェース定義が面倒です。もっと簡単な方法はありませんか?

A. no-undefルールを無効化する方法がありますが、推奨しません。
型チェックの緩和はバグの原因になる可能性があるため、最初からインターフェースを定義することをおすすめします。

まとめ

今回の問題から得た教訓をまとめます。

  1. ESLintとTypeScriptは別物

    • ESLintはTypeScriptのtriple-slash directiveを認識しません
  2. 明示的な型定義で解決

    • 必要なプロパティのみを持つインターフェースを定義することで、両方の要件を満たせます
  3. 実用的なアプローチ

    • 実際の型と互換性があるため、既存のコードにそのまま適用できます
  4. 同じ問題が他の型でも発生

    • Cloudflare Workersのグローバル型すべてで同様の問題が発生する可能性があります

この方法を採用したことで、Lintエラーが解消され、開発体験が改善されました。

確認手順

  1. npm run lint でエラーがないことを確認
  2. npm run test でテストが通ることを確認
  3. TypeScriptコンパイルエラーがないことを確認

参考リンク


{ "@context": "https://schema.org", "@type": "TechArticle", "headline": "ESLintがCloudflare Workers型を認識しない問題の解決", "description": "Cloudflare WorkersでR2を使用する際、ESLintがR2Object型を認識しないno-undefエラーの原因と解決方法を解説。", "image": "https://wakatchi.dev/wp-content/uploads/2026/01/eslint-cloudflare-workers.png", "datePublished": "2026-01-01", "dateModified": "2026-01-01", "author": { "@type": "Person", "name": "wakatchi" }, "publisher": { "@type": "Organization", "name": "wakatchi.dev", "logo": { "@type": "ImageObject", "url": "https://wakatchi.dev/wp-content/uploads/logo.png" } }, "keywords": "ESLint, TypeScript, Cloudflare Workers, R2, 型定義, no-undef", "articleSection": "Infrastructure", "proficiencyLevel": "Beginner", "dependencies": "ESLint, TypeScript, @cloudflare/workers-types", "operatingSystem": "Any", "programmingLanguage": "TypeScript" }