This guide shows you how to handle the response from a fetch call in TypeScript without losing type safety or crashing at runtime. You’ll learn to validate the raw JSON, narrow unknown types, and avoid common pitfalls like trusting an unvalidated type assertion. By the end, you’ll have a reusable pattern for typed, safe data fetching.
What You’ll Need
- TypeScript 4.4 or later (for
unknownandsatisfiessupport) - A basic understanding of generics and type guards
- An environment where
fetchis available (browser or Node.js 18+)
Step-by-Step
- Define your expected response type. Create an interface or type that describes the shape you expect from the API. For example, if you’re fetching a user, define
interface User { id: number; name: string; email: string }. - Create a type guard for that type. Write a function that checks if an unknown value matches your type. Use
typeof,Array.isArray, and property checks. For example:function isUser(value: unknown): value is User { ... }. This is the core of safe typing—it narrows the type at runtime. - Call
fetchand parse the response. Useawait fetch(url), then checkresponse.okto catch HTTP errors. Parse withawait response.json(), which returnsPromise<any>. Assign the result to a variable typed asunknownto force validation. - Validate the parsed data with your type guard. Pass the
unknownvalue to your guard. If it returnstrue, TypeScript narrows the type to your expected type. Iffalse, throw an error or handle it gracefully. - Wrap the whole thing in a try/catch. Handle network errors, JSON parse errors, and validation failures in one place. This prevents unhandled rejections and gives you a single error path.
- Create a reusable generic function. Abstract the pattern into a function like
async function fetchTyped<T>(url: string, guard: (data: unknown) => data is T): Promise<T>. This way you only write the validation logic once and reuse it for different endpoints. - Use the typed data. After the function returns, you can safely access properties with full autocomplete and type checking, because the data is guaranteed to be of type
T.
Common Mistakes
- Using
asto cast the response.const data = await response.json() as Usertells TypeScript to trust you, but if the API changes or returns an error object, you’ll get runtime errors that TypeScript can’t catch. Always validate. - Ignoring
response.ok. A 404 or 500 still returns a response object. If you don’t checkok, you’ll try to parse an error page as JSON and likely get a syntax error or invalid data. - Assuming the JSON structure is flat. Nested objects need their own guards. Validate each level to avoid
undefinedproperty access. - Forgetting that arrays are objects. In a guard, check
Array.isArray(value)before checking properties, otherwise you might treat an array as an object. - Not handling
response.json()errors. If the response body isn’t valid JSON,json()throws. Wrap it in try/catch or check the content-type header first.
Tips
- Use a validation library for complex schemas. Libraries like Zod or io-ts can generate type guards automatically from a schema, saving time and reducing errors. They also provide better error messages.
- Keep guards small and composable. Write a guard for a single object, then combine them for larger structures. This makes testing easier.
- Use
unknowninstead ofany.unknownforces you to check the type before using it, whileanydisables type checking entirely. - Throw a custom error class. Create a
ValidationErrorwith details about what failed. This helps debugging in production. - Consider caching the parsed response. If you fetch the same data multiple times, store the validated result to avoid re-parsing.
Troubleshooting
- TypeScript still shows
anyafter validation. Make sure your guard returns a type predicate (value is T), not just a boolean. If it returnsboolean, TypeScript won’t narrow the type. - Property access fails on the validated data. Your guard might be too permissive. Add more checks: verify the type of each property, not just that it exists.
- Fetch throws a TypeError in Node.js. This often means the URL is malformed or the network is unreachable. Check the URL and ensure your environment has network access.
- JSON parse error on a valid response. The server might be returning empty body or HTML. Check the
Content-Typeheader and log the raw text withawait response.text()to debug. - Validation fails for optional fields. If a property can be
nullor missing, update your guard to allowundefined. Useif ('property' in value)to check existence.
Frequently Asked Questions
- Why can’t I just use
asto type the response? Becauseasis a compile-time assertion that doesn’t check the actual data. If the API returns unexpected data, your code will fail at runtime with no warning. - Do I need a type guard for every API call? Not necessarily. If you trust the API and the cost of a runtime error is low, you might skip it. But for critical data, guards are worth the effort.
- What’s the difference between
unknownandany?unknownis the safe counterpart ofany. You must narrow it before using it;anybypasses type checking entirely. - Can I use a class with a type guard? Yes, you can use
instanceofin a guard for class instances. But for plain JSON data, interfaces are more common. - How do I handle array responses? Create a guard for the element type, then check
Array.isArray(value) && value.every(isElement). - Is it okay to use a library like Zod? Absolutely. Libraries like Zod generate types from schemas, so you get both validation and type inference in one place. They’re especially useful for large projects.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Poco C51 Airtel Prepaid Locked Variant India?
- Jenkins vs GitHub Actions: Which CI/CD Fits Your Team?
- Turn Off Backup Encryption in iTunes?
- Campus Tech Innovation Hubs: What They Are
- Shared VPS Hosting: Is It Better?
- Disk Defrag Pro: What It Does and Whether You Need It