Skip to main content
Version: v7.0.0

Typescript - Extend Request

In Hyper-Fetch, you can create highly reusable functions and components by building them to work with generic RequestInstance objects. This approach allows you to enforce specific type constraints, ensuring that any request passed to your component has the expected shape—for example, a response that is an array of items for an autocomplete component.

This guide will walk you through how to extend requests using TypeScript generics to build flexible, type-safe, and modular features.

What you'll learn
  1. How to create generic functions that accept RequestInstance objects.
  2. How to use TypeScript generics to enforce type constraints on requests.
  3. A practical example of building a reusable autocomplete function.
  4. The benefits of this pattern for creating type-safe and modular code.

The Core Concept

The key to this pattern is TypeScript's generics. By creating a function or component that accepts a generic type, you can set constraints on what kind of requests are allowed. By default, you can create a generic function that accepts any request instance like this:

import { RequestInstance } from "@hyper-fetch/core";

function someFunction<T extends RequestInstance>(request: T) {
// This function will accept any Request passed as a parameter.
}

However, the real power comes from making this more specific. The RequestInstance type from @hyper-fetch/core is a generic type itself, allowing you to specify the expected response, payload, queryParams, and error types. For many reusable components, you might only care about the response type.

Practical Example: Autocomplete

Let's build on this concept with a generic autocomplete function. We want this function to accept any request that returns an array of strings (string[]) in its response. Any other response type should result in a TypeScript error.

First, let's define two requests: one that fits our criteria and one that does not.

// We assume 'client' is a pre-configured Client instance
// For example:
// import { createClient } from "@hyper-fetch/core";
// const client = createClient({ url: "..." });

// This request's response is `string[]`, which is what our function expects.
const getSuggestions = client.createRequest<{ response: string[] }>()({
endpoint: "suggestions",
});

// This request's response is `string`, which should be rejected.
const getUsername = client.createRequest<{ response: string }>()({
endpoint: "username",
});

Now, let's create our generic autocomplete function. We'll use a generic constraint to ensure the request parameter has a response type of string[].

function autocomplete<Request extends RequestInstance<{ response: string[] }>>(request: Request) {
// Now, inside this function, TypeScript knows that `request.send()`
// will eventually return a `data` property of type `string[]` on success.
console.log("Request received, can be used for fetching suggestions.");
return request.send();
}

With this setup, TypeScript will enforce our constraint at compile time.

// ✅ Correct Usage
// This works because `getSuggestions` has a response type of `string[]`.
autocomplete(getSuggestions);

// ⛔ Incorrect Usage
// This will cause a TypeScript error because `getUsername`'s response is `string`, not `string[]`.
autocomplete(getUsername);

When to Use It?

This pattern is powerful for building reusable, type-safe components and functions. Use it for:

  • Generic UI Components like <DataTable /> or <SelectField />.
  • Type-safe utilities for data processing.
  • Abstracting business logic into reusable functions.

By using this approach, you promote code reuse and maintain strong type safety across your application.


Congratulations!

You've learned how to extend Hyper-Fetch requests for building flexible and type-safe components!

  • You can create generic functions that constrain request types.
  • You understand how to enforce a specific response shape on a request.
  • You are able to build reusable components that are decoupled from specific API endpoints.