From b70c667b12721da0600585d7e6d477b776ec1288 Mon Sep 17 00:00:00 2001 From: productdevbook Date: Mon, 30 Mar 2026 07:47:01 +0300 Subject: [PATCH] feat: add isFetchError type guard Add `isFetchError()` utility function for type-safe error narrowing in catch blocks. Returns `true` for FetchError instances and narrows the type accordingly. Resolves #460 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/error.ts | 4 ++++ test/index.test.ts | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/error.ts b/src/error.ts index f1625f60..86a9ce53 100644 --- a/src/error.ts +++ b/src/error.ts @@ -65,3 +65,7 @@ export function createFetchError( return fetchError; } + +export function isFetchError(error: unknown): error is FetchError { + return error instanceof FetchError; +} diff --git a/test/index.test.ts b/test/index.test.ts index 5ac20b07..69a9041a 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -9,7 +9,7 @@ import { } from "vitest"; import { Readable } from "node:stream"; import { H3, HTTPError, readBody, serve } from "h3"; -import { $fetch } from "../src/index.ts"; +import { $fetch, FetchError, isFetchError } from "../src/index.ts"; describe("ofetch", () => { let listener: ReturnType; @@ -524,4 +524,37 @@ describe("ofetch", () => { timeout: 10_000, }); }); + + describe("isFetchError", () => { + it("returns true for FetchError instances", async () => { + const error = await $fetch(getURL("404")).catch((error_: any) => error_); + expect(isFetchError(error)).toBe(true); + expect(error).toBeInstanceOf(FetchError); + }); + + it("returns false for non-FetchError values", () => { + expect(isFetchError(new Error("test"))).toBe(false); + // eslint-disable-next-line unicorn/no-null + expect(isFetchError(null)).toBe(false); + expect(isFetchError(undefined)).toBe(false); + expect(isFetchError("string")).toBe(false); + expect(isFetchError({})).toBe(false); + }); + + it("narrows type correctly", async () => { + const error: unknown = await $fetch(getURL("403")).catch( + (error_: any) => error_ + ); + if (isFetchError(error)) { + expect(error.status).toBe(403); + expect(error.statusText).toBe("Forbidden"); + expect(error.data).toBeDefined(); + expect(error.request).toBeDefined(); + expect(error.options).toBeDefined(); + expect(error.response).toBeDefined(); + } else { + throw new Error("Expected FetchError"); + } + }); + }); });