diff --git a/infinite/index.ts b/infinite/index.ts index b6600576e..c754985bf 100644 --- a/infinite/index.ts +++ b/infinite/index.ts @@ -41,7 +41,7 @@ export const infinite = ((( ): SWRInfiniteResponse => { const rerender = useState({})[1] const didMountRef = useRef(false) - const dataRef = useRef() + const dataRef = useRef>() const { cache, @@ -167,7 +167,12 @@ export const infinite = ((( const mutate = useCallback( ( - data: Data[] | undefined | Promise | MutatorCallback, + data?: + | Data[] + | Readonly + | Promise + | Promise> + | MutatorCallback, shouldRevalidate = true ) => { // It is possible that the key is still falsy. @@ -190,7 +195,9 @@ export const infinite = ((( ) // Function to load pages data from the cache based on the page size. - const resolvePagesFromCache = (pageSize: number): Data[] | undefined => { + const resolvePagesFromCache = ( + pageSize: number + ): Readonly | undefined => { // return an array of page data const data: Data[] = [] diff --git a/src/types.ts b/src/types.ts index dfe67fb69..d4222fa29 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,7 @@ import * as revalidateEvents from './constants/revalidate-events' -export type FetcherResponse = Data | Promise +type Async = Data | Promise +export type FetcherResponse = Async export type Fetcher = /** @@ -142,8 +143,8 @@ export type Arguments = string | null | ArgumentsTuple | Record export type Key = Arguments | (() => Arguments) export type MutatorCallback = ( - currentValue?: Data -) => Promise | undefined | Data + currentValue?: Readonly +) => Async> export type Broadcaster = ( cache: Cache, @@ -160,32 +161,35 @@ export type State = { isValidating?: boolean } +type MutatorData = + | Async + | Async> + | MutatorCallback + export type Mutator = ( cache: Cache, key: Key, - data?: Data | Promise | MutatorCallback, + data?: MutatorData, shouldRevalidate?: boolean -) => Promise +) => Promise | undefined> export interface ScopedMutator { /** This is used for bound mutator */ - ( - key: Key, - data?: Data | Promise | MutatorCallback, - shouldRevalidate?: boolean - ): Promise + (key: Key, data?: MutatorData, shouldRevalidate?: boolean): Promise< + Readonly | undefined + > /** This is used for global mutator */ ( key: Key, - data?: T | Promise | MutatorCallback, + data?: MutatorData, shouldRevalidate?: boolean - ): Promise + ): Promise | undefined> } export type KeyedMutator = ( - data?: Data | Promise | MutatorCallback, + data?: MutatorData, shouldRevalidate?: boolean -) => Promise +) => Promise | undefined> // Public types @@ -196,10 +200,10 @@ export type SWRConfiguration< > = Partial> export interface SWRResponse { - data?: Data - error?: Error - mutate: KeyedMutator + data?: Readonly + error?: Readonly isValidating: boolean + mutate: KeyedMutator } export type KeyLoader = diff --git a/test/type/state.ts b/test/type/state.ts new file mode 100644 index 000000000..56fb44a66 --- /dev/null +++ b/test/type/state.ts @@ -0,0 +1,35 @@ +import useSWR from 'swr' + +type ExpectType = (value: T) => void +const expectType: ExpectType = () => {} + +export function useInferDataType() { + // Should infer the data type + expectType(useSWR('foo', i => i).data) + expectType(useSWR('foo', () => 1).data) + expectType<{ foo: number } | undefined>(useSWR({ foo: 1 }, i => i).data) + expectType<{ foo: 1 } | undefined>(useSWR({ foo: 1 } as const, i => i).data) + expectType(useSWR(['foo'], i => i[0]).data) + + // Should mark data and error as readonly + expectType | undefined>(useSWR({}, i => i).data) + expectType | undefined>(useSWR('').error) +} + +export function useInferMutateType() { + // Should infer the mutate argument type + useSWR('foo', i => i).mutate(v => { + expectType(v) + return v + }) + + // Should mark it as readonly + useSWR({ foo: 1 }, i => i).mutate(v => { + expectType | undefined>(v) + return v + }) + useSWR({ foo: 1 } as const, i => i).mutate(v => { + expectType | undefined>(v) + return v + }) +} diff --git a/test/use-swr-local-mutation.test.tsx b/test/use-swr-local-mutation.test.tsx index c6767e7eb..98e2a647c 100644 --- a/test/use-swr-local-mutation.test.tsx +++ b/test/use-swr-local-mutation.test.tsx @@ -44,7 +44,7 @@ describe('useSWR - local mutation', () => { const { data: state, mutate: setState } = useSWR(`${baseKey}--${key}`, { fallbackData }) - return [state, setState] + return [state, setState] as const } function Page() {