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.jsonでglobals設定にR2Objectを追加する方法もあります。
{
"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ルールを無効化する方法がありますが、推奨しません。
型チェックの緩和はバグの原因になる可能性があるため、最初からインターフェースを定義することをおすすめします。
まとめ
今回の問題から得た教訓をまとめます。
-
ESLintとTypeScriptは別物
- ESLintはTypeScriptのtriple-slash directiveを認識しません
-
明示的な型定義で解決
- 必要なプロパティのみを持つインターフェースを定義することで、両方の要件を満たせます
-
実用的なアプローチ
- 実際の型と互換性があるため、既存のコードにそのまま適用できます
-
同じ問題が他の型でも発生
- Cloudflare Workersのグローバル型すべてで同様の問題が発生する可能性があります
この方法を採用したことで、Lintエラーが解消され、開発体験が改善されました。
確認手順
npm run lint でエラーがないことを確認
npm run test でテストが通ることを確認
- 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"
}