🚫

【TypeScript】@typescript-eslint/no-explicit-any エラーと型安全ガイド

に公開

はじめに

TypeScriptを使っていると、よく目にするこんなコンパイルエラーがあります。

Error: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any

型安全を保つためのルールですが、エラーが度重なると無効化したいと思う瞬間も多いでしょう。この記事では、このルールを無効化せずに共存する方法を紹介します。

対象者

  • TypeScriptとESLintを用いて開発しているエンジニア
  • any問題や型定義で悩んでいる方
  • コンパイルエラーや型崩れを経験した方

1. 基礎知識:no-explicit-anyルール

なぜこのルールが重要なのか

TypeScriptの強みは「静的型付けによる安心感」です。しかし、anyを使いすぎるとその恩恵が失われ、最終的にJavaScriptと変わらなくなってしまいます。そのため、no-explicit-anyルールは型安全を守るための役割を果たします。

ルールの設定方法

ESLintルール @typescript-eslint/no-explicit-any は、「any」型の利用を警告または禁止するルールです。設定は .eslintrc に記述します。
.eslintrc.jsonでの基本設定:

// .eslintrc.json
{
  "rules": {
    "@typescript-eslint/no-explicit-any": "error"
  }
}

エラーが発生するケース

上記の設定により、以下のようなコードがエラーになります。

// エラー:明示的なanyの使用
function processData(data: any) {
  return data.someProperty;
}

// エラー:配列でのany使用
const items: any[] = [];

// エラー:ジェネリクスでのany使用
const cache = new Map<string, any>();

2. 無効化の落とし穴と推奨アプローチ

ESLintルールを無効化するのは簡単ですが、その影響はプロジェクト全体に波及します。
この設定を行うと、どこでも any が許可され、型安全性が失われます。短期的には便利ですが、長期的には型崩壊が進行します。

// ⚠️ 推奨されない設定
{
  "rules": {
    "@typescript-eslint/no-explicit-any": "off"
  }
}

推奨アプローチ

段階的な対応として、以下のような手法で意図的な例外であることを明示することが望ましいです。

  • 無効化ではなく、例外ルールを設ける
  • 局所的にのみ使用し、明示的な意図をコメントで残す
// 一時的な例外として、意図を明確にコメント
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 外部ライブラリの型定義待ち
function handleLegacyLibrary(data: any) {
  // 処理内容
}

3. 代替案

anyの代わりに使える代替案としては、以下のような実装方法が挙げられます。

解決策1:unknown型で安全に受け取る

unknownは「型安全なany」として機能します

// 推奨:unknown型の活用
async function fetchApiData(url: string): Promise<unknown> {
  const response = await fetch(url);
  return response.json();
}

// 使用時は型ガードやアサーションが必要
async function getUserData() {
  const data = await fetchApiData('/api/user');
  
  // 型ガードによる安全な型絞り込み
  if (isUserData(data)) {
    console.log(data.name); // 型安全にアクセス可能
  }
}

// 型ガード関数
function isUserData(data: unknown): data is { name: string; id: number } {
  return (
    typeof data === 'object' &&
    data !== null &&
    'name' in data &&
    'id' in data
  );
}

解決策2:Record型で構造を定義

オブジェクト型が確実な場合

// キーと値の型を明確にする
type ApiResponse = Record<string, unknown>;

function processApiResponse(response: ApiResponse) {
  // 各プロパティはunknownなので、使用時に型チェックが必要
  const status = response['status'];
  if (typeof status === 'string') {
    console.log(status.toUpperCase());
  }
}

解決策3:ジェネリクスで柔軟性を確保

// ジェネリクスで型安全と柔軟性を両立
class ApiClient {
  async get<T = unknown>(url: string): Promise<T> {
    const response = await fetch(url);
    return response.json() as T;
  }
}

// 使用例
interface User {
  id: number;
  name: string;
}

const client = new ApiClient();
const user = await client.get<User>('/api/user/1');
console.log(user.name); // 型安全にアクセス

4. ZodやPrismaと組み合わせた型安全戦略

ライブラリ概要

  • Zod: データのバリデーションと型推論を同時に行うライブラリ
  • Prisma: 型安全なORMで、DBスキーマから型を自動生成

これらを組み合わせることで、データの型定義を一元化できます。
こうすることで、anyの登場余地が消え、API層とDB層の型整合性も維持されます。

5. 段階的導入戦略

すべてのanyを禁止するのは非現実的です。以下のように段階的に厳格化するのがおすすめです。

フェーズ別設定

// .eslintrc.json
{
  "overrides": [
    {
      "files": ["src/legacy/**/*.ts"],
      "rules": {
        "@typescript-eslint/no-explicit-any": "warn"
      }
    },
    {
      "files": ["src/new/**/*.ts"],
      "rules": {
        "@typescript-eslint/no-explicit-any": "error"
      }
    }
  ]
}

まとめ

@typescript-eslint/no-explicit-anyは、TypeScriptプロジェクトの型安全性を守る重要なルールです。
適切なツールと戦略を用いることで型崩壊を防ぎます。

アプローチ例

  1. unknown型:不明な型を安全に扱う
  2. ジェネリクス:柔軟性と型安全性の両立
  3. Zod:ランタイム検証による確実な型安全性
  4. 段階的導入:チームの負担を最小化

参考リンク


おわりに

no-explicit-anyルールとは、Zod・Prisma・ジェネリクス・unknownなどや段階的導入戦略をうまく組み合わせることで、型安全性を保ちながら開発を進めることができます。型安全なTypeScriptプロジェクトの構築に、この記事が一助となれば幸いです。


株式会社ONE WEDGE

【Serverlessで世の中をもっと楽しく】
ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp/

GitHubで編集を提案

Discussion