From 651e09bc31a45f5f02682c358831ec4097c9cbc2 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Sat, 19 Dec 2020 12:10:45 +0300 Subject: [PATCH] feat: extract types from endpoint chore: change example and readme Closes #24 --- README.md | 2 +- examples/movies/src/endpoint.ts | 4 ++-- examples/movies/src/pages.tsx | 6 +++--- src/endpoint.ts | 6 +++++- src/lazy-request-hook.ts | 13 +++++++++---- src/request-hook.ts | 11 ++++++++--- target/endpoint.d.ts | 5 ++++- target/lazy-request-hook.d.ts | 6 +++--- target/request-hook.d.ts | 4 ++-- 9 files changed, 37 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 8b3c95a..1e00c45 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ const MoviesEndpoint: Endpoint = { type MoviesResponse = Movie[]; function App() { - const { data, loading } = useRequest(MoviesEndpoint); + const { data, loading } = useRequest(MoviesEndpoint); return !data ? (
{ loading ? 'Loading...' : 'Something went wrong' }
diff --git a/examples/movies/src/endpoint.ts b/examples/movies/src/endpoint.ts index 498505e..31910b0 100644 --- a/examples/movies/src/endpoint.ts +++ b/examples/movies/src/endpoint.ts @@ -21,11 +21,11 @@ export type MoviesResponse = { items: Movie[], } -export const MovieEndpoint: Endpoint = { +export const MovieEndpoint: Endpoint = { method: Method.GET, url: ({ id }) => `/action-adventure/${id}`, }; export type MovieParams = Readonly<{ id: React.Key }>; -export type MovieResponse = Movie[]; +export type MovieResponse = Movie; diff --git a/examples/movies/src/pages.tsx b/examples/movies/src/pages.tsx index 80fd9ca..c385f5a 100644 --- a/examples/movies/src/pages.tsx +++ b/examples/movies/src/pages.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { useRequest } from 'react-rest-request'; import { Link, useParams } from 'react-router-dom'; -import { MovieEndpoint, MovieParams, MovieResponse, MoviesEndpoint, MoviesResponse } from './endpoint'; +import { MovieEndpoint, MoviesEndpoint } from './endpoint'; export function MoviesPage() { - const { data, loading } = useRequest(MoviesEndpoint); + const { data, loading } = useRequest(MoviesEndpoint); return !data ? (
{ loading ? 'Loading...' : 'Something went wrong' }
@@ -27,7 +27,7 @@ export function MoviesPage() { export function MoviePage() { const params = useParams<{ id: string}>(); - const { data, loading } = useRequest( + const { data, loading } = useRequest( MovieEndpoint, { params, diff --git a/src/endpoint.ts b/src/endpoint.ts index 0dd4417..8550238 100644 --- a/src/endpoint.ts +++ b/src/endpoint.ts @@ -8,9 +8,13 @@ export enum Method { DELETE = 'DELETE', } -export type Endpoint = Readonly<{ +export type Endpoint = Readonly<{ method: Method; url: string | ((params: P) => string); headers?: Record; transformResponseData?: (data: any) => R; }> + +export type ExtractEndpointResponse = E extends Endpoint ? R : E extends Endpoint ? R : never; +export type ExtractEndpointVariables = E extends Endpoint ? V : E extends Endpoint ? V : never; +export type ExtractEndpointParams = E extends Endpoint ? P : never; diff --git a/src/lazy-request-hook.ts b/src/lazy-request-hook.ts index 4691d16..7c1b85b 100644 --- a/src/lazy-request-hook.ts +++ b/src/lazy-request-hook.ts @@ -2,13 +2,13 @@ import React, { useCallback, useMemo } from 'react'; import invariant from 'tiny-invariant'; import isEqual from 'lodash.isequal'; import { useClient } from './client-hook'; -import { Endpoint } from './endpoint'; +import { Endpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables } from './endpoint'; import { PublicRequestState, RequestAction, requestReducer, RequestState } from './reducer'; import { useRequestContext } from './request-context'; import { ClientResponse } from './client'; import { isFunction } from './misc'; -export type LazyRequestConfig = Readonly<{ +export type LazyRequestConfig = Readonly<{ variables?: V; params?: P; headers?: Record; @@ -23,8 +23,13 @@ export type LazyRequestHandlerConfig = Readonly< export type RequestHandler = (config?: LazyRequestHandlerConfig) => Promise; -export function useLazyRequest, V = Record, P = void>( - endpoint: Endpoint, +export function useLazyRequest< + E extends Endpoint, + R = ExtractEndpointResponse, + V = ExtractEndpointVariables, + P = ExtractEndpointParams +>( + endpoint: E, config?: LazyRequestConfig, ): [RequestHandler, PublicRequestState] { const [client] = useClient(); diff --git a/src/request-hook.ts b/src/request-hook.ts index 367e064..376e529 100644 --- a/src/request-hook.ts +++ b/src/request-hook.ts @@ -1,6 +1,6 @@ import React from 'react'; import invariant from 'tiny-invariant'; -import { Endpoint, Method } from './endpoint'; +import { Endpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables, Method } from './endpoint'; import { LazyRequestConfig, useLazyRequest } from './lazy-request-hook'; export type RequestConfig = Readonly< @@ -10,8 +10,13 @@ export type RequestConfig = Readonly< } > -export function useRequest, V = Record, P = void>( - endpoint: Endpoint, +export function useRequest< + E extends Endpoint, + R = ExtractEndpointResponse, + V = ExtractEndpointVariables, + P = ExtractEndpointParams +>( + endpoint: E, config?: RequestConfig, ) { invariant( diff --git a/target/endpoint.d.ts b/target/endpoint.d.ts index cb6246e..4996fea 100644 --- a/target/endpoint.d.ts +++ b/target/endpoint.d.ts @@ -6,9 +6,12 @@ export declare enum Method { PATCH = "PATCH", DELETE = "DELETE" } -export declare type Endpoint = Readonly<{ +export declare type Endpoint = Readonly<{ method: Method; url: string | ((params: P) => string); headers?: Record; transformResponseData?: (data: any) => R; }>; +export declare type ExtractEndpointResponse = E extends Endpoint ? R : E extends Endpoint ? R : never; +export declare type ExtractEndpointVariables = E extends Endpoint ? V : E extends Endpoint ? V : never; +export declare type ExtractEndpointParams = E extends Endpoint ? P : never; diff --git a/target/lazy-request-hook.d.ts b/target/lazy-request-hook.d.ts index 40cf41a..ad4b0f9 100644 --- a/target/lazy-request-hook.d.ts +++ b/target/lazy-request-hook.d.ts @@ -1,7 +1,7 @@ -import { Endpoint } from './endpoint'; +import { Endpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables } from './endpoint'; import { PublicRequestState } from './reducer'; import { ClientResponse } from './client'; -export declare type LazyRequestConfig = Readonly<{ +export declare type LazyRequestConfig = Readonly<{ variables?: V; params?: P; headers?: Record; @@ -12,4 +12,4 @@ export declare type LazyRequestHandlerConfig = Readonly; export declare type RequestHandler = (config?: LazyRequestHandlerConfig) => Promise; -export declare function useLazyRequest, V = Record, P = void>(endpoint: Endpoint, config?: LazyRequestConfig): [RequestHandler, PublicRequestState]; +export declare function useLazyRequest, R = ExtractEndpointResponse, V = ExtractEndpointVariables, P = ExtractEndpointParams>(endpoint: E, config?: LazyRequestConfig): [RequestHandler, PublicRequestState]; diff --git a/target/request-hook.d.ts b/target/request-hook.d.ts index 8235d5b..8b372b0 100644 --- a/target/request-hook.d.ts +++ b/target/request-hook.d.ts @@ -1,9 +1,9 @@ -import { Endpoint } from './endpoint'; +import { Endpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables } from './endpoint'; import { LazyRequestConfig } from './lazy-request-hook'; export declare type RequestConfig = Readonly & { skip?: boolean; }>; -export declare function useRequest, V = Record, P = void>(endpoint: Endpoint, config?: RequestConfig): Pick, R = ExtractEndpointResponse, V = ExtractEndpointVariables, P = ExtractEndpointParams>(endpoint: E, config?: RequestConfig): Pick