Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: default to fetch type in keyed mutator #2753

Merged
merged 3 commits into from
Sep 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions _internal/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,10 @@ export interface ScopedMutator {
* @typeParam Data - The type of the data related to the key
* @typeParam MutationData - The type of the data returned by the mutator
*/
export type KeyedMutator<Data> = <MutationData>(
data?:
| MutationData
| Promise<MutationData | undefined>
| MutatorCallback<MutationData>,
export type KeyedMutator<Data> = <MutationData = Data>(
data?: Data | Promise<Data | undefined> | MutatorCallback<Data>,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@promer94 Why was this change reverted? It makes the generic completely useless and returning different MutationData to be passed to populateCache impossible with proper types.

This probably needs to be overload based to function properly, otherwise MutationData will just be inferred improperly from the data arg, although it is mostly possible to do without overloads with some rather extensive types:

type KeyedMutator<Data> = <MutationData = Data>(data?: MutationData | Promise<MutationData | undefined> | MutatorCallback<MutationData>, opts?: boolean | Omit<MutatorOptions<Data, MutationData>, 'populateCache'> & (MutationData extends Data ? Pick<MutatorOptions<Data, Data>, 'populateCache'> : { populateCache: Exclude<MutatorOptions<Data, MutationData>['populateCache'], boolean> })) => Promise<Data | MutationData | undefined>;

I have tested the above and it works as expected, except in the case where opts is not passed or is a boolean. This is why I suggested the overload approach, since if options is not passed as an object, typescript doesn't enforce it being passed.

opts?: boolean | MutatorOptions<Data, MutationData>
) => Promise<MutationData | undefined>
) => Promise<Data | MutationData | undefined>

export type SWRConfiguration<
Data = any,
Expand Down
12 changes: 8 additions & 4 deletions infinite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,12 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>

const mutate = useCallback(
// eslint-disable-next-line func-names
function <T>(
data?: undefined | T | Promise<T | undefined> | MutatorCallback<T>,
function <T = Data[]>(
data?:
| undefined
| Data[]
| Promise<Data[] | undefined>
| MutatorCallback<Data[]>,
opts?: undefined | boolean | MutatorOptions<Data[], T>
) {
// When passing as a boolean, it's explicitly used to disable/enable
Expand All @@ -257,8 +261,8 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
}

return arguments.length
? swr.mutate<T>(data, { ...options, revalidate: shouldRevalidate })
: swr.mutate<T>()
? swr.mutate(data, { ...options, revalidate: shouldRevalidate })
: swr.mutate()
},
// swr.mutate is always the same reference
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
4 changes: 3 additions & 1 deletion test/type/mutate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,11 @@ export function useMutatorTypes() {

mutate(async () => '1')

// @ts-expect-error
mutate(async () => 1)

mutate(async () => 1, { populateCache: false })
// FIXME: this should work.
// mutate(async () => 1, { populateCache: false })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@promer94 thanks for adding the test, maybe we should create an issue to follow up on this? it feels like a regression for mutate() API types

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original pr #2708 did not fix this correctly so there is no regression here.

I also think we should not support this because the type implementation would be really complicated and not useful in practice. You should always make sure you data has same structure as cache when using the bounded mutate fn.

I just keep it here as a reference.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right the type is number, I updated them in #2781

}

export function useConfigMutate() {
Expand Down
5 changes: 3 additions & 2 deletions test/use-swr-local-mutation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1580,10 +1580,11 @@ describe('useSWR - local mutation', () => {
}

function Page() {
const { data, mutate } = useSWR(key, () => serverData)
const { mutate } = useSWRConfig()
const { data } = useSWR(key, () => serverData)

appendData = () => {
return mutate(sendRequest('cherry'), {
return mutate<string[], string>(key, sendRequest('cherry'), {
optimisticData: [...data, 'cherry (optimistic)'],
populateCache: (result, currentData) => [
...currentData,
Expand Down
Loading