Search code examples
typescriptzod

How to fix `Unsafe argument of type `any` assigned to a parameter of type `string`.` in Zod?


Actually for some strange reason, I am getting one error locally (with zod v3.22.4), and another error on stackblitz, using v3.22.4, so not sure, maybe both errors need fixing.

The stackblitz example, which is the same code I have locally, is this:

import { z } from 'zod';

type BuildCommandToDecompressWithUnarchiver = {
  overwrite?: boolean;
  password?: string;
  output: {
    directory: {
      path: string;
    };
  };
  input: {
    file: {
      path: string;
    };
  };
};

const BuildCommandToDecompressWithUnarchiverModel: z.ZodType<BuildCommandToDecompressWithUnarchiver> =
  z.object({
    overwrite: z.optional(z.boolean()).default(false),
    password: z.optional(z.string()),
    output: z.object({
      directory: z.object({
        path: z.string(),
      }),
    }),
    input: z.object({
      file: z.object({
        path: z.string(),
      }),
    }),
  });

export function buildCommandToDecompressWithUnarchiver(source) {
  const input = BuildCommandToDecompressWithUnarchiverModel.parse(source);
  const cmd = [
    `unar`,
    `${input.input.file.path}`,
    `-o`,
    `${input.output.directory.path}`,
  ];

  cmd.push('--quiet');

  if (input.overwrite) {
    cmd.push(`-f`);
  }

  if (input.password) {
    cmd.push(`-p`, input.password);
  }

  return cmd;
}

console.log(
  BuildCommandToDecompressWithUnarchiverModel.parse({
    output: {
      directory: {
        path: 'foo',
      },
    },
    input: {
      file: {
        path: 'x.zip',
      },
    },
  })
);

First off, sidenote. I need z.ZodType<BuildCommandToDecompressWithUnarchiver> because in many of my definitions, I use nested schemas, and you have to often use this pattern to get it to compile. So that needs to stay.

But what I'm getting in stackblitz is:

Type 'ZodObject<{ overwrite: ZodDefault<ZodOptional<ZodBoolean>>; password: ZodOptional<ZodString>; output: ZodObject<{ directory: ZodObject<{ path: ZodString; }, "strip", ZodTypeAny, { ...; }, { ...; }>; }, "strip", ZodTypeAny, { ...; }, { ...; }>; input: ZodObject<...>; }, "strip", ZodTypeAny, { ...; }, { ...; }>' is not assignable to type 'ZodType<BuildCommandToDecompressWithUnarchiver, ZodTypeDef, BuildCommandToDecompressWithUnarchiver>'.
  The types of '_type.output.directory' are incompatible between these types.
    Type '{ path?: string; }' is not assignable to type '{ path: string; }'.
      Property 'path' is optional in type '{ path?: string; }' but required in type '{ path: string; }'.(2322)

I don't see anywhere I am defining path as optional, so where is it getting that from?

But locally, I see a different error:

Unsafe argument of type `any` assigned to a parameter of type `string`.eslint@typescript-eslint/no-unsafe-argument
(property) password?: string

enter image description here

I have that "Unsafe argument of type any assigned to a parameter of type string" error in a dozen+ places in my codebase, all using the same pattern for defining zod schemas. Any idea how to fix the last one (and possibly why stackblitz isn't reproducing and is instead showing a different error, which I also sometimes get locally)?

My way around the last error is to just do input.password as string, but that is not right... It's already guaranteed typed a string by this point.


Solution

  • As for the error on stackbliz, I'm pretty sure it's a bug in the typescript version, you just need to use the keyword as to fix it:

    
    
    const BuildCommandToDecompressWithUnarchiverModel: z.ZodType<BuildCommandToDecompressWithUnarchiver> =
      z.object({
        overwrite: z.optional(z.boolean()).default(false),
        password: z.optional(z.string()),
        output: z.strictObject({
          directory: z.object({
            path: z.string(),
          }).required(),
        }),
        input: z.object({
          file: z.object({
            path: z.string(),
          }).required(),
        }).required(),
      }) as z.ZodType<BuildCommandToDecompressWithUnarchiver>;
    
    

    for local above error code. That's because of an eslint rule of @typescript-eslint that doesn't allow indeterminate types. You can safely disable it with:

    // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
    

    or

    const cmd: string[] = [
      // ...
    ]