Types
Types
Complete TypeScript type definitions and interfaces provided by @shkumbinhsn/fetcher.
Core Types
FetcherRequestInit<T>
Extended version of the standard RequestInit interface with additional properties for schema validation and error handling.
interface FetcherRequestInit<TResponse extends StandardSchemaV1 | undefined = undefined> extends RequestInit { schema?: TResponse; errors?: ApiErrorStatic<any>[];}Properties:
- Includes all standard
RequestInitproperties (method,headers,body, etc.) schema?: TResponse- Optional response validation schemaerrors?: ApiErrorStatic<any>[]- Optional array of custom error classes
Example:
import type { FetcherRequestInit } from '@shkumbinhsn/fetcher';
const config: FetcherRequestInit<typeof UserSchema> = { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData), schema: UserSchema, errors: [NotFoundError, ValidationError]};InferResponse<T>
Utility type that infers the response type from a schema or returns any if no schema is provided.
type InferResponse<T> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : any;Usage:
import type { InferResponse } from '@shkumbinhsn/fetcher';import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), name: z.string(), email: z.string()});
// Type is { id: string; name: string; email: string }type User = InferResponse<typeof UserSchema>;
// Type is anytype AnyResponse = InferResponse<undefined>;Error Types
ApiError
Base class for all API errors. All custom error classes created with defineError() extend this class.
class ApiError extends Error { statusCode: number; data: unknown; response: Response;
constructor(message: string, data: unknown, response: Response);}Properties:
statusCode: number- HTTP status code from the responsedata: unknown- Validated error response data (typed in subclasses)response: Response- Original fetch Response objectmessage: string- Error message (inherited from Error)name: string- Error class name (inherited from Error)
Example:
import { ApiError } from '@shkumbinhsn/fetcher';
try { await fetcher('/api/endpoint', { errors: [CustomError] });} catch (error) { if (error instanceof ApiError) { console.log(error.statusCode); // HTTP status console.log(error.response); // Original Response console.log(error.data); // Error response data }}ApiErrorStatic<T>
Type representing the constructor/static interface of custom error classes.
interface ApiErrorStatic<TSchema extends StandardSchemaV1> { new (message: string, data: StandardSchemaV1.InferOutput<TSchema>, response: Response): ApiError & { data: StandardSchemaV1.InferOutput<TSchema> };
statusCode: number; schema: TSchema;}This type is used internally and typically doesn’t need to be used directly in application code.
SchemaValidationError
Error thrown when response validation fails.
class SchemaValidationError extends Error { issues: unknown[];
constructor(message: string, issues: unknown[]);}Properties:
issues: unknown[]- Validation issues from the schema librarymessage: string- Error message describing the validation failure
Utility Types
Custom Request Init Extensions
You can extend FetcherRequestInit for your specific use cases:
import type { FetcherRequestInit } from '@shkumbinhsn/fetcher';
// Add custom propertiesinterface CustomRequestInit<T> extends FetcherRequestInit<T> { retries?: number; timeout?: number; cache?: boolean;}
// Use in functionsasync function customFetch<T>( url: string, init?: CustomRequestInit<T>) { const { retries, timeout, cache, ...fetchInit } = init || {}; // Handle custom logic return fetcher(url, fetchInit);}API Client Types
Create strongly typed API clients:
import type { FetcherRequestInit, InferResponse } from '@shkumbinhsn/fetcher';
// Define endpoint configurationinterface ApiEndpoint<TSchema, TInput = never> { path: string; method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; schema: TSchema; input?: TInput;}
// Extract response type from endpointtype EndpointResponse<T> = T extends ApiEndpoint<infer TSchema, any> ? InferResponse<TSchema> : never;
// Extract input type from endpointtype EndpointInput<T> = T extends ApiEndpoint<any, infer TInput> ? TInput : never;
// Example endpoint definitionsconst endpoints = { getUser: { path: '/users/:id', method: 'GET', schema: UserSchema } as const,
createUser: { path: '/users', method: 'POST', schema: UserSchema, input: CreateUserSchema } as const} as const;
// Typed API call functionasync function callEndpoint<K extends keyof typeof endpoints>( key: K, ...args: EndpointInput<typeof endpoints[K]> extends never ? [pathParams?: Record<string, string>] : [input: EndpointInput<typeof endpoints[K]>, pathParams?: Record<string, string>]): Promise<EndpointResponse<typeof endpoints[K]>> { // Implementation here}Error Union Types
Create union types for error handling:
// Union of all possible API errorstype ApiError = | InstanceType<typeof NotFoundError> | InstanceType<typeof ValidationError> | InstanceType<typeof UnauthorizedError>;
// Type guard for API errorsfunction isApiError(error: unknown): error is ApiError { return error instanceof NotFoundError || error instanceof ValidationError || error instanceof UnauthorizedError;}
// Safe API call wrapperasync function safeApiCall<T>( apiCall: () => Promise<T>): Promise<{ data: T } | { error: ApiError }> { try { const data = await apiCall(); return { data }; } catch (error) { if (isApiError(error)) { return { error }; } throw error; // Re-throw non-API errors }}Schema Integration Types
Schema Library Types
The library works with any Standard Schema compatible library:
import type { StandardSchemaV1 } from '@standard-schema/spec';
// Zodimport { z } from 'zod';const zodSchema: StandardSchemaV1 = z.object({ name: z.string() });
// Valibotimport * as v from 'valibot';const valibotSchema: StandardSchemaV1 = v.object({ name: v.string() });
// ArkTypeimport { type } from 'arktype';const arkTypeSchema: StandardSchemaV1 = type({ name: 'string' });Schema Response Mapping
Map different schemas to their response types:
// Schema registryconst schemas = { user: UserSchema, users: z.array(UserSchema), createUser: CreateUserSchema, updateUser: UpdateUserSchema} as const;
// Extract response typestype SchemaResponses = { [K in keyof typeof schemas]: InferResponse<typeof schemas[K]>};
// Results in:// {// user: User;// users: User[];// createUser: CreateUserInput;// updateUser: UpdateUserInput;// }Advanced Type Patterns
Conditional Request Types
Create types that change based on the HTTP method:
type MethodBasedInit< TMethod extends string, TSchema> = TMethod extends 'GET' | 'DELETE' ? Omit<FetcherRequestInit<TSchema>, 'body'> // No body for GET/DELETE : FetcherRequestInit<TSchema>; // Body allowed for others
function typedFetch< TSchema, TMethod extends 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' = 'GET'>( url: string, method: TMethod, init?: MethodBasedInit<TMethod, TSchema>) { return fetcher(url, { method, ...init });}Generic API Response Wrapper
Handle API responses that wrap data:
// Common API response patterninterface ApiResponse<T> { success: boolean; data: T; message?: string; errors?: string[];}
// Schema for wrapped responsesconst createWrappedSchema = <T extends StandardSchemaV1>(dataSchema: T) => z.object({ success: z.boolean(), data: dataSchema, message: z.string().optional(), errors: z.array(z.string()).optional() });
// Type helpertype WrappedResponse<T> = T extends StandardSchemaV1 ? ApiResponse<InferResponse<T>> : never;
// Usageconst wrappedUserSchema = createWrappedSchema(UserSchema);type WrappedUser = WrappedResponse<typeof UserSchema>; // ApiResponse<User>Type-Safe Configuration
Environment-Based Types
Create types that change based on environment:
interface BaseConfig { baseUrl: string; timeout: number;}
interface DevelopmentConfig extends BaseConfig { debug: true; mockDelay: number;}
interface ProductionConfig extends BaseConfig { debug: false; apiKey: string;}
type Config = DevelopmentConfig | ProductionConfig;
function createFetcher(config: Config) { return async function<T>(url: string, init?: FetcherRequestInit<T>) { // Type-safe configuration usage if (config.debug) { console.log('Making request to:', config.baseUrl + url); // config.mockDelay is available here (TypeScript knows it's DevelopmentConfig) } else { // config.apiKey is available here (TypeScript knows it's ProductionConfig) }
return fetcher(config.baseUrl + url, init); };}Related
- fetcher() - Main function using these types
- defineError() - Function for creating error types
- TypeScript Integration - Advanced TypeScript usage patterns