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

Type Safety And Consistency Issues #2

Open
gamemann opened this issue Aug 1, 2023 · 0 comments
Open

Type Safety And Consistency Issues #2

gamemann opened this issue Aug 1, 2023 · 0 comments
Labels
help wanted Extra attention is needed

Comments

@gamemann
Copy link
Member

gamemann commented Aug 1, 2023

There are two issues related to type safety when building Deaconn via next build. While these errors can be safely ignored and the website will operate without any major issues because they only appear with stricter overrides to the compiler options, this solution isn't ideal because we shouldn't be using variables and objects of type any anywhere with TypeScript. I will admit this is a bigger concern with another project of mine also utilizing the same frameworks used for Deaconn (Best Mods). The temporary solution involves commenting out the following configuration from the .eslintrc.cjs file.

overrides: [
  {
    extends: [
      "plugin:@typescript-eslint/recommended-requiring-type-checking"
    ],
    files: ["*.ts", "*.tsx"],
    parserOptions: {
      project: "tsconfig.json"
    }
  }
]

Passing Formik Form As any Type

The first issue relates to src/components/forms/main.tsx.

import React from "react";

import { FormikProvider } from "formik";

const Main: React.FC<{
    form: any,
    children: React.ReactNode,
    submitBtn: JSX.Element,
    type?: string
}> = ({
    form,
    children,
    submitBtn,
    type="POST"
}) => {
    return (
        <FormikProvider value={form}>
            <form method={type} onSubmit={form.handleSubmit}>
                {children}
                {submitBtn}
            </form>
        </FormikProvider>
    );
}

export default Main;

Errors are as follows.

./src/components/forms/main.tsx
6:11  Warning: Unexpected any. Specify a different type.  @typescript-eslint/no-explicit-any
17:32  Error: Unsafe assignment of an `any` value.  @typescript-eslint/no-unsafe-assignment
18:43  Error: Unsafe assignment of an `any` value.  @typescript-eslint/no-unsafe-assignment
18:43  Error: Unsafe member access .handleSubmit on an `any` value.  @typescript-eslint/no-unsafe-member-access

We simply need to specify a valid type for the form parameter. However, this is more complicated since this components acts as a wrapper for all of our forms meaning the form types are different each time (e.g. different initiated values).

Reparsing Objects & Variables Via JSON.parse() When Passing As Prop

When passing a prop from Next's getServerSideProps() async function, it requires being able to serialize the prop's values via JSON. For example, say we have the following code.

export async function getServerSideProps(ctx: GetServerSidePropsContext) {
    let article: Article | null = null;

    /* Retrieve article... */

    return {
        props: {
            article: article
        }
    }
}

The Article type includes a Date object field (createdAt) resulting in the above causing the following error.

Error: Error serializing `.article.createdAt` returned from `getServerSideProps` in "...".
Reason: `object` ("[object Date]") cannot be serialized as JSON. Please only return JSON serializable data types.

The solution I've always used was converting the object to a JSON string and then parsing the string as a JSON object. Typically, you'd use JavaScript's JSON.stringify() and JSON.parse() functions. However, JSON.parse() returns any which results in the following Next error when having the configuration mentioned at the beginning of the issue uncommented.

48:13  Error: Unsafe assignment of an `any` value.  @typescript-eslint/no-unsafe-assignment

With that said, using JSON.parse() results in the createdAt field being set to a string instead of a Date object which is invalid type safety. There are a couple of things I've thought of doing before realizing this. I was initially trying to create a JSON validator that would validate JSON.parse() against a passed type and return the object as that specific type, but that was difficult (read below). However, as just stated, JSON.parse() sets the Date object to a string which renders the validator useless since we have another problem.

I believe I'm going to need to create new types for each object we pass via props that contains a string field (e.g. createdAtStr) that is set to the createdAt field as a string from the Date object. The components receiving the prop already parses the date as a string (e.g. const articleDate = new Date(article?.createdAtStr ?? Date.now())), so this should be all we need to do.

Looking Into Validating JSON.parse() (No Good)

I've read Stack Overflow threads (1, 2), but there isn't an easy solution without writing a lot of validation code alongside JSON.parse(). This answer from Stack Overflow is the best I could find and even then it's complicated. There are other options I explored such as using ZOD validation. However, since there are a lot of times we use this method to pass props along with many different types, I wasn't able to find something that easily converts a typical TypeScript type to a ZOD schema without writing out a schema for each type which would be quite annoying in my opinion.

I'll need to research these issues further and once I have updates, I will reply to this issue.

@gamemann gamemann added the help wanted Extra attention is needed label Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

1 participant