Closed bfovez closed 2 months ago
Thanks, this is actually a bug with the TypeScript plugin, not the documentation. We will look into it!
Is there any update on this? Still seems to be an issue in the latest version
Any update on this? Still getting it
Is there a way to disable the rule ts(71007)
globally?
It's more troublesome than it's worth. There's already a runtime exception thrown if anything other than a server action is passed as prop to a client component from a server component.
Having this rule enabled seems to make it opinionated that you can't have components with "use client"
to have optional functions as props, which you may choose to pass depending on whether you use it in server component or another client component.
I noticed that if you make it optional, the warning goes away.
updateItem?: (data: FormData) => Promise<void>
// OR
// Note: Remember the needed bracket enclosure, otherwise the meaning is different.
updateItem: ((data: FormData) => Promise<void>) | undefined
But still, it's confusing. Is there a way to distinguish server actions from regular functions? Like by using a hypothetical ServerAction
type?
// HYPOTHETICAL CODE - DO NOT COPY
import type { ServerAction } from 'next'
...
updateItem: ServerAction<(data: FormData) => Promise<void>>
// HYPOTHETICAL CODE - DO NOT COPY
This can help to type-check that only server actions are passed to the client as props (not any vanilla function).
Also, the docs don't have a TypeScript example for the part where it says "You can also pass a Server Action to a Client Component as a prop".
I found a workaround until the bug is fixed without making it optional. It's still not ideal especially since I couldn't get it to work with generics, but as a stopgap you could create many of these for any flavor of action (FormData
as input returning void
, specific params returning some optional error message, etc)
I'm not sure why this works because inspecting the end type still shows an async function ((data: FormData) => Promise<void>
), but typescript is cool with it now.
src/actions/index.ts
"use server";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function serverAction(data: FormData): Promise<void> {
throw new Error("Not implemented");
}
export type ServerAction = typeof serverAction;
src/components/Something.tsx
"use client"
import { ServerAction } from '@/actions'
// or
// import type { ServerAction } from '@/actions'
export const Something = ({ action }: { action: ServerAction }) => (
<form action={action}>
<input name="something" />
</form>
)
I found a workaround until the bug is fixed without making it optional. It's still not ideal especially since I couldn't get it to work with generics, but as a stopgap you could create many of these for any flavor of action (
FormData
as input returningvoid
, specific params returning some optional error message, etc)
This seems to work with generics.
export type ServerAction<T> = ((data: T) => Promise<void>) & Function
// in client component
export const TheForm = ({ myAction }: { myAction: ServerAction<{ hey: string }> }) => {
return (
<form action={myAction.bind(undefined, { hey: 'you' })}>
<button>Submit</button>
</form>
)
}
// in server component
<TheForm
myAction={async data => {
'use server'
console.info(data)
}}
/>
is there any update regarding this issue? I found that this work around resulting in this error message
index.d.ts(3202, 9): The expected type comes from property 'action' which is declared here on type 'DetailedHTMLProps<FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>'
Server action
Client Component
Is there a proper way to resolve this?
The current plan by the React team is to establish the pattern of using the Action
suffix as a hint that a prop is an Action. We're going to leverage that convention in the Next.js TypeScript plugin:
It's not perfect since you could still pass a function that's not a Server function. But we still have runtime type-checking to catch that.
Looks good
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.
What is the improvement or update you wish to see?
The docs here https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#props shows how to pass pass down a Server Action to a Client Component as a prop.
But as soon as we use typescript instead of plain javascript, things must be typed:
example-server-component.tsx:
example-client-component.tsx:
Then, we get an error from typescript in [the client](example-client-component.tsx:
Props must be serializable for components in the "use client" entry file, "updateItem" is invalid.ts(71007)
However, it somehow works anyway.
The docs should be more explicit about how the things must be properly done with typescript.
Is there any context that might help us understand?
How to implement the example of this part of the docs pass down a Server Action to a Client Component as a prop with typescript.
Does the docs page already exist? Please link to it.
https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#props
NEXT-1524