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.
- How to create generic functions that accept
RequestInstance
objects. - How to use TypeScript generics to enforce type constraints on requests.
- A practical example of building a reusable autocomplete function.
- 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.
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.