examples icon indicating copy to clipboard operation
examples copied to clipboard

Typecheck fails on blog-tutorial example

Open advaiyalad opened this issue 3 years ago • 2 comments

When I run typechecking or have my editor open, the following errors are shown by typescript, even on a fresh clone of the example:

> typecheck
> tsc -b && tsc -b cypress

app/routes/notes/$noteId.tsx:36:16 - error TS2352: Conversion of type 'SerializeObject<Simplify<{ note: Note; } & {}>>' to 
type 'LoaderData' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, conv
ert the expression to 'unknown' first.
  The types of 'note.createdAt' are incompatible between these types.
    Type 'string' is not comparable to type 'Date'.

36   const data = useLoaderData() as LoaderData;
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

app/routes/posts/$slug.tsx:23:26 - error TS2352: Conversion of type 'SerializeObject<Simplify<{ post: Post; html: string; }
 & {}>>' to type 'LoaderData' may be a mistake because neither type sufficiently overlaps with the other. If this was inten
tional, convert the expression to 'unknown' first.
  The types of 'post.createdAt' are incompatible between these types.
    Type 'string' is not comparable to type 'Date'.

23   const { post, html } = useLoaderData() as LoaderData;
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

app/routes/posts/admin.tsx:16:21 - error TS2352: Conversion of type 'SerializeObject<Simplify<{ posts: Post[]; } & {}>>' to
 type 'LoaderData' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, con
vert the expression to 'unknown' first.
  Types of property 'posts' are incompatible.
    Type 'SerializeObject<Simplify<{ createdAt: Date; updatedAt: Date; title: string; slug: string; markdown: string; } & {
}>>[]' is not comparable to type 'Post[]'.
      Type 'SerializeObject<Simplify<{ createdAt: Date; updatedAt: Date; title: string; slug: string; markdown: string; } &
 {}>>' is not comparable to type 'Post'.

16   const { posts } = useLoaderData() as LoaderData;
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

app/routes/posts/index.tsx:18:21 - error TS2352: Conversion of type 'SerializeObject<Simplify<{ posts: Post[]; } & {}>>' to
 type 'LoaderData' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, con
vert the expression to 'unknown' first.
  Types of property 'posts' are incompatible.
    Type 'SerializeObject<Simplify<{ createdAt: Date; updatedAt: Date; title: string; slug: string; markdown: string; } & {
}>>[]' is not comparable to type 'Post[]'.

18   const { posts } = useLoaderData() as LoaderData;
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Found 4 errors.

What seems to be happening is that createdAt is a string rather than a date somewhere in between (in Prisma, maybe). A temporary workaround is to convert the result of useLoaderData to unknown and then LoaderData, but this isn't the best solution. This problem happens regardless of the TS version (both 4.7 and 4.8 result in an error). Is there a known solution to this?

advaiyalad avatar Oct 19 '22 01:10 advaiyalad

It's always difficult to keep a tutorial updated with the latest changes (especially on a fast-paced project like Remix).

The "current" recommended approach to typing loaders is as follows:

  • use LoaderArgs instead of LoaderFunction
  • always wrap return value in json helper
  • use useLoaderArgs<typeof loader>() instead of as LoaderData
import { json, type LoaderArgs } from '@remix-run/node'
import { db } from '~/db.server'

export const loader = async ({request, params}: LoaderArgs => {
  const { jokeId } = params
  const joke = await db.joke.findUnique({
    where: { id: jokeId },
  })
  return json({joke})
}

export default function Joke() {
  const { joke } = useLoaderData<typeof loader>()
  return <div>{ joke.name }</div>
}

NOTE: Since Remix always returns loader data as serialized JSON, values like Date will be converted to a string. So the inferred type will by string not Date. If you would like to maintain the native types, then you can use remix-typedjson which will maintain the native types across the entire request.

https://github.com/kiliman/remix-typedjson

kiliman avatar Oct 19 '22 18:10 kiliman

@PythonCreator27 I'm currently working on a PR that will update these old usages to the newly recommended one

MichaelDeBoey avatar Oct 20 '22 15:10 MichaelDeBoey

It would be great if has a codemod to update all the examples

franklinjavier avatar Nov 17 '22 21:11 franklinjavier

@franklinjavier I thought about creating a codemod for this, but since it has so many edge-cases that aren't straight-forward, it's better to do it manually yourself in each codebase

MichaelDeBoey avatar Nov 17 '22 21:11 MichaelDeBoey

@franklinjavier I thought about creating a codemod for this, but since it has so many edge-cases that aren't straight-forward, it's better to do it manually yourself in each codebase

agree

franklinjavier avatar Nov 28 '22 19:11 franklinjavier