diff --git a/infinite/index.ts b/infinite/index.ts index eac8d06c0..2fc261957 100644 --- a/infinite/index.ts +++ b/infinite/index.ts @@ -120,7 +120,7 @@ export const infinite = ((useSWRNext: SWRHook) => let previousPageData = null for (let i = 0; i < pageSize; ++i) { - const [pageKey, pageArgs] = serialize(getKey(i, previousPageData)) + const [pageKey, pageArg] = serialize(getKey(i, previousPageData)) if (!pageKey) { // `pageKey` is falsy, stop fetching new pages. @@ -148,7 +148,7 @@ export const infinite = ((useSWRNext: SWRHook) => !config.compare(originalData[i], pageData)) if (fn && shouldFetchPage) { - pageData = await fn(...pageArgs) + pageData = await fn(pageArg) cache.set(pageKey, pageData) } diff --git a/infinite/types.ts b/infinite/types.ts index 845218fc6..7cfd0d3a6 100644 --- a/infinite/types.ts +++ b/infinite/types.ts @@ -6,14 +6,10 @@ export type SWRInfiniteFetcher< Data = any, KeyLoader extends SWRInfiniteKeyLoader = SWRInfiniteKeyLoader > = KeyLoader extends (...args: any[]) => any - ? ReturnType extends - | readonly [...infer K] - | null - | false - | undefined - ? (...args: [...K]) => FetcherResponse + ? ReturnType extends readonly [...infer T] + ? (args: T) => FetcherResponse : ReturnType extends infer T | null | false | undefined - ? (...args: [T]) => FetcherResponse + ? (args: T) => FetcherResponse : never : never diff --git a/src/types.ts b/src/types.ts index 541267709..0b028541c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,16 +7,16 @@ export type BareFetcher = ( export type Fetcher< Data = unknown, SWRKey extends Key = Key -> = SWRKey extends () => readonly [...infer Args] | null | undefined | false - ? (...args: [...Args]) => FetcherResponse +> = SWRKey extends () => readonly [...infer Args] + ? (args: Args) => FetcherResponse : SWRKey extends readonly [...infer Args] - ? (...args: [...Args]) => FetcherResponse + ? (args: Args) => FetcherResponse : SWRKey extends () => infer Arg | null | undefined | false - ? (...args: [Arg]) => FetcherResponse + ? (args: Arg) => FetcherResponse : SWRKey extends null | undefined | false ? never : SWRKey extends infer Arg - ? (...args: [Arg]) => FetcherResponse + ? (args: Arg) => FetcherResponse : never // Configuration types that are only used internally, not exposed to the user. diff --git a/src/use-swr.ts b/src/use-swr.ts index 53573f00b..b5f1e350b 100644 --- a/src/use-swr.ts +++ b/src/use-swr.ts @@ -33,6 +33,18 @@ import { const WITH_DEDUPE = { dedupe: true } +type DefinitelyTruthy = false extends T + ? never + : 0 extends T + ? never + : '' extends T + ? never + : null extends T + ? never + : undefined extends T + ? never + : T + export const useSWRHandler = ( _key: Key, fetcher: Fetcher | null, @@ -55,9 +67,9 @@ export const useSWRHandler = ( // `key` is the identifier of the SWR `data` state, `keyInfo` holds extra // states such as `error` and `isValidating` inside, // all of them are derived from `_key`. - // `fnArgs` is an array of arguments parsed from the key, which will be passed + // `fnArg` is the argument/arguments parsed from the key, which will be passed // to the fetcher. - const [key, fnArgs, keyInfo] = serialize(_key) + const [key, fnArg, keyInfo] = serialize(_key) // If it's the initial render of this hook. const initialMountedRef = useRef(false) @@ -205,7 +217,11 @@ export const useSWRHandler = ( } // Start the request and save the timestamp. - FETCH[key] = [currentFetcher(...fnArgs), getTimestamp()] + // Key must be truthly if entering here. + FETCH[key] = [ + currentFetcher(fnArg as DefinitelyTruthy), + getTimestamp() + ] } // Wait until the ongoing request is done. Deduplication is also @@ -342,7 +358,7 @@ export const useSWRHandler = ( return true }, - // `setState` is immutable, and `eventsCallback`, `fnArgs`, `keyInfo`, + // `setState` is immutable, and `eventsCallback`, `fnArg`, `keyInfo`, // and `keyValidating` are depending on `key`, so we can exclude them from // the deps array. // diff --git a/src/utils/serialize.ts b/src/utils/serialize.ts index 331ebee94..b4233ac93 100644 --- a/src/utils/serialize.ts +++ b/src/utils/serialize.ts @@ -3,7 +3,7 @@ import { isFunction } from './helper' import { Key } from '../types' -export const serialize = (key: Key): [string, any[], string] => { +export const serialize = (key: Key): [string, Key, string] => { if (isFunction(key)) { try { key = key() @@ -13,7 +13,9 @@ export const serialize = (key: Key): [string, any[], string] => { } } - const args = [].concat(key as any) + // Use the original key as the argument of fether. This can be a stirng or an + // array of values. + const args = key // If key is not falsy, or not an empty array, hash it. key = diff --git a/test/type/fetcher.ts b/test/type/fetcher.ts index 0c1a7bdf3..e1b1ebb4a 100644 --- a/test/type/fetcher.ts +++ b/test/type/fetcher.ts @@ -58,20 +58,17 @@ export function useRecord() { } export function useTuple() { - useSWR([{ a: '1', b: { c: '3' } }, [1231, '888']], (...keys) => { + useSWR([{ a: '1', b: { c: '3' } }, [1231, '888']], keys => { + expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) + return keys + }) + useSWR(truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : null, keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys }) - useSWR( - truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : null, - (...keys) => { - expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) - return keys - } - ) useSWR( truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : false, - (...keys) => { + keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } @@ -79,7 +76,7 @@ export function useTuple() { } export function useReadonlyTuple() { - useSWR([{ a: '1', b: { c: '3' } }, [1231, '888']] as const, (...keys) => { + useSWR([{ a: '1', b: { c: '3' } }, [1231, '888']] as const, keys => { expectType< [ { @@ -95,7 +92,7 @@ export function useReadonlyTuple() { }) useSWR( truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : null, - (...keys) => { + keys => { expectType< [ { @@ -112,7 +109,7 @@ export function useReadonlyTuple() { ) useSWR( truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : false, - (...keys) => { + keys => { expectType< [ { @@ -250,14 +247,14 @@ export function useReturnRecord() { export function useReturnTuple() { useSWR( () => [{ a: '1', b: { c: '3' } }, [1231, '888']], - (...keys) => { + keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } ) useSWR( () => (truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : null), - (...keys) => { + keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } @@ -265,7 +262,7 @@ export function useReturnTuple() { useSWR( () => (truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : false), - (...keys) => { + keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } @@ -273,7 +270,7 @@ export function useReturnTuple() { useSWRInfinite( index => [{ a: '1', b: { c: '3', d: index } }, [1231, '888']], - (...keys) => { + keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys[1] } @@ -282,7 +279,7 @@ export function useReturnTuple() { useSWRInfinite( index => truthy() ? [{ a: '1', b: { c: '3', d: index } }, [1231, '888']] : null, - (...keys) => { + keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys[1] } @@ -291,7 +288,7 @@ export function useReturnTuple() { useSWRInfinite( index => truthy() ? [{ a: '1', b: { c: '3', d: index } }, [1231, '888']] : false, - (...keys) => { + keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys[1] } @@ -301,7 +298,7 @@ export function useReturnTuple() { export function useReturnReadonlyTuple() { useSWR( () => [{ a: '1', b: { c: '3' } }, [1231, '888']] as const, - (...keys) => { + keys => { expectType< [ { @@ -319,9 +316,9 @@ export function useReturnReadonlyTuple() { useSWR( () => truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : null, - (...keys) => { + keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { @@ -338,9 +335,9 @@ export function useReturnReadonlyTuple() { useSWR( () => truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : false, - (...keys) => { + keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { @@ -356,9 +353,9 @@ export function useReturnReadonlyTuple() { useSWRInfinite( () => [{ a: '1', b: { c: '3' } }, [1231, '888']] as const, - (...keys) => { + keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { @@ -374,9 +371,9 @@ export function useReturnReadonlyTuple() { useSWRInfinite( () => truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : null, - (...keys) => { + keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { @@ -393,9 +390,9 @@ export function useReturnReadonlyTuple() { useSWRInfinite( () => truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : false, - (...keys) => { + keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { diff --git a/test/type/option-fetcher.ts b/test/type/option-fetcher.ts index 2bb955ca6..4e34a19ac 100644 --- a/test/type/option-fetcher.ts +++ b/test/type/option-fetcher.ts @@ -57,19 +57,19 @@ export function useRecord() { export function useTuple() { useSWR([{ a: '1', b: { c: '3' } }, [1231, '888']], { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } }) useSWR(truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : null, { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } }) useSWR(truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : false, { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } @@ -78,7 +78,7 @@ export function useTuple() { export function useReadonlyTuple() { useSWR([{ a: '1', b: { c: '3' } }, [1231, '888']] as const, { - fetcher: (...keys) => { + fetcher: keys => { expectType< [ { @@ -96,7 +96,7 @@ export function useReadonlyTuple() { useSWR( truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : null, { - fetcher: (...keys) => { + fetcher: keys => { expectType< [ { @@ -115,7 +115,7 @@ export function useReadonlyTuple() { useSWR( truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : false, { - fetcher: (...keys) => { + fetcher: keys => { expectType< [ { @@ -259,13 +259,13 @@ export function useReturnRecord() { export function useReturnTuple() { useSWR(() => [{ a: '1', b: { c: '3' } }, [1231, '888']], { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } }) useSWR(() => (truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : null), { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } @@ -274,7 +274,7 @@ export function useReturnTuple() { useSWR( () => (truthy() ? [{ a: '1', b: { c: '3' } }, [1231, '888']] : false), { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys } @@ -284,7 +284,7 @@ export function useReturnTuple() { useSWRInfinite( index => [{ a: '1', b: { c: '3', d: index } }, [1231, '888']], { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys[1] } @@ -295,7 +295,7 @@ export function useReturnTuple() { index => truthy() ? [{ a: '1', b: { c: '3', d: index } }, [1231, '888']] : null, { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys[1] } @@ -306,7 +306,7 @@ export function useReturnTuple() { index => truthy() ? [{ a: '1', b: { c: '3', d: index } }, [1231, '888']] : false, { - fetcher: (...keys) => { + fetcher: keys => { expectType<[{ a: string; b: { c: string } }, (string | number)[]]>(keys) return keys[1] } @@ -316,7 +316,7 @@ export function useReturnTuple() { export function useReturnReadonlyTuple() { useSWR(() => [{ a: '1', b: { c: '3' } }, [1231, '888']] as const, { - fetcher: (...keys) => { + fetcher: keys => { expectType< [ { @@ -335,9 +335,9 @@ export function useReturnReadonlyTuple() { () => truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : null, { - fetcher: (...keys) => { + fetcher: keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { @@ -356,9 +356,9 @@ export function useReturnReadonlyTuple() { () => truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : false, { - fetcher: (...keys) => { + fetcher: keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { @@ -374,7 +374,7 @@ export function useReturnReadonlyTuple() { ) useSWRInfinite(() => [{ a: '1', b: { c: '3' } }, [1231, '888']] as const, { - fetcher: (...keys) => { + fetcher: keys => { expectType< [ { @@ -393,9 +393,9 @@ export function useReturnReadonlyTuple() { () => truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : null, { - fetcher: (...keys) => { + fetcher: keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { @@ -414,9 +414,9 @@ export function useReturnReadonlyTuple() { () => truthy() ? ([{ a: '1', b: { c: '3' } }, [1231, '888']] as const) : false, { - fetcher: (...keys) => { + fetcher: keys => { expectType< - [ + readonly [ { readonly a: '1' readonly b: { diff --git a/test/use-swr-infinite.test.tsx b/test/use-swr-infinite.test.tsx index 9cbbc57d2..66d9d4e5a 100644 --- a/test/use-swr-infinite.test.tsx +++ b/test/use-swr-infinite.test.tsx @@ -74,7 +74,7 @@ describe('useSWRInfinite', () => { function Page() { const { data, size, setSize } = useSWRInfinite( index => [key, index], - (_, index) => createResponse(`page ${index}, `) + ([_, index]) => createResponse(`page ${index}, `) ) useEffect(() => { @@ -101,7 +101,7 @@ describe('useSWRInfinite', () => { function Page() { const { data, mutate: boundMutate } = useSWRInfinite( index => [key, index], - (_, index) => createResponse(`${pageData[index]}, `), + ([_, index]) => createResponse(`${pageData[index]}, `), { initialSize: 3 } @@ -212,7 +212,7 @@ describe('useSWRInfinite', () => { function Page() { const { data, size, setSize } = useSWRInfinite( index => [key, index], - (_, index) => { + ([_, index]) => { requests++ return createResponse(`page ${index}, `) } @@ -262,7 +262,7 @@ describe('useSWRInfinite', () => { function Page() { const { data, size, setSize } = useSWRInfinite( index => [key, index], - (_, index) => { + ([_, index]) => { requests++ return createResponse(`page ${index}, `) }, @@ -315,7 +315,7 @@ describe('useSWRInfinite', () => { function Page() { const { data, size, setSize } = useSWRInfinite( index => [key, index], - (_, index) => createResponse(`page ${index}, `) + ([_, index]) => createResponse(`page ${index}, `) ) return ( @@ -364,7 +364,7 @@ describe('useSWRInfinite', () => { const [t, setT] = useState(false) const { data, size, setSize } = useSWRInfinite( index => [key, index, t ? 'A' : 'B'], - (_, index) => createResponse(`page ${index}, `) + ([_, index]) => createResponse(`page ${index}, `) ) toggle = setT @@ -403,7 +403,7 @@ describe('useSWRInfinite', () => { const [t, setT] = useState(false) const { data, size, setSize } = useSWRInfinite( index => [key, index, t ? 'A' : 'B'], - async (_, index) => createResponse(`page ${index}, `), + async ([_, index]) => createResponse(`page ${index}, `), { persistSize: true } @@ -445,7 +445,7 @@ describe('useSWRInfinite', () => { function Comp() { const { data, size, setSize } = useSWRInfinite( index => [key, index], - (_, index) => createResponse(`page ${index}, `) + ([_, index]) => createResponse(`page ${index}, `) ) return ( @@ -491,7 +491,7 @@ describe('useSWRInfinite', () => { function Comp() { const { data, size, setSize } = useSWRInfinite( index => [key, index], - (_, index) => createResponse(`page ${index}, `) + ([_, index]) => createResponse(`page ${index}, `) ) setters.push(setSize) @@ -580,7 +580,7 @@ describe('useSWRInfinite', () => { index => { return [key, `/api?page=${index + 1}`] }, - (_, index) => { + ([_, index]) => { requests.push(index) return createResponse(dummyResponses[index]) }, @@ -621,7 +621,7 @@ describe('useSWRInfinite', () => { const useCustomSWRInfinite = () => { const { data, setSize, size } = useSWRInfinite( index => [key, `/api?page=${index + 1}`], - (_, index) => createResponse(dummyResponses[index]) + ([_, index]) => createResponse(dummyResponses[index]) ) return { data: data ? [].concat(...data) : [], @@ -735,7 +735,7 @@ describe('useSWRInfinite', () => { let mutate function Comp() { mutate = useSWRConfig().mutate - const { data, size, setSize } = useSWRInfinite(getKey, (_, index) => + const { data, size, setSize } = useSWRInfinite(getKey, ([_, index]) => createResponse(`page ${index}, `) ) useEffect(() => { @@ -827,7 +827,7 @@ describe('useSWRInfinite', () => { function Comp() { const { data, size, setSize } = useSWRInfinite( index => [key, index], - (_, index) => createResponse(`page ${index}`) + ([_, index]) => createResponse(`page ${index}`) ) return ( diff --git a/test/use-swr-integration.test.tsx b/test/use-swr-integration.test.tsx index 0143e0970..2f9ae8f67 100644 --- a/test/use-swr-integration.test.tsx +++ b/test/use-swr-integration.test.tsx @@ -279,7 +279,10 @@ describe('useSWR', () => { const key1 = createKey() const key2 = createKey() function Page() { - const { data: v1 } = useSWR([key1, obj, arr], (a, b, c) => a + b.v + c[0]) + const { data: v1 } = useSWR( + [key1, obj, arr], + ([a, b, c]) => a + b.v + c[0] + ) // reuse the cache const { data: v2 } = useSWR([key1, obj, arr], () => 'not called!') @@ -287,7 +290,7 @@ describe('useSWR', () => { // different object const { data: v3 } = useSWR( [key2, obj, 'world'], - (a, b, c) => a + b.v + c + ([a, b, c]) => a + b.v + c ) return ( @@ -312,7 +315,7 @@ describe('useSWR', () => { function Page() { const { data } = useSWR( () => [key, obj, arr], - (a, b, c) => a + b.v + c[0] + ([a, b, c]) => a + b.v + c[0] ) return
{data}
diff --git a/test/use-swr-key.test.tsx b/test/use-swr-key.test.tsx index 19f95d4f6..47eb15328 100644 --- a/test/use-swr-key.test.tsx +++ b/test/use-swr-key.test.tsx @@ -114,7 +114,7 @@ describe('useSWR - key', () => { let updateId - const fetcher = fn => fn() + const fetcher = ([fn]) => fn() function Page() { const [id, setId] = React.useState('first')