profile
viewpoint

Ask questionsGeneric for parsing objects

Really enjoy the library! Thank you for the work!

To easier debug when a parse fails, I made a small wrapper like this:

function parse(parser, obj) {
  try {
    return parser.parse(obj);
  } catch (e) {
    console.log(obj);
    throw e;
  }
}

The problem is that typescript can not infer this function.

I tried multiple combinations but can not seem to get it right.

I looked through issues already mentioning generic and did fine one small example: https://github.com/vriad/zod/issues/93 but it did not help fully.

function parse<K, T extends z.ZodType<K, any>>(parser: T, obj): K {
  try {
    return parser.parse(obj);
  } catch (e) {
    console.log(obj);
    throw e;
  }
}
function parse<T extends z.ZodType<any, any>, K typeof T>(parser: T, obj): K {
  try {
    return parser.parse(obj);
  } catch (e) {
    console.log(obj);
    throw e;
  }
}
interface Schema<T> {
  parse: (data:unknown): T;
  check: (data:unknown): data is T;
}
function parse<T extends Schema<K>>(parser: T, obj) {
  try {
    return parser.parse(obj);
  } catch (e) {
    console.log(obj);
    throw e;
  }
}
interface SchemaVal {
  parse<K>(data: unknown): K;
}

function parse<T extends SchemaVal>(parser: T, obj): K {
  try {
    return parser.parse(obj);
  } catch (e) {
    console.log(obj);
    throw e;
  }
}
vriad/zod

Answer questions chrbala

I have written this which has somewhat different goals as the above example, which may be of interest to people:

  • Intentional (validation) errors returned, not thrown. This distinguishes them from unintentional errors which are still thrown.
  • Semi-strongly typed errors (properties known, but not enumerated).
  • Always async
  • Not concerned with creating factories, can curry if desired
  • Decoupled from Zod. Returns errors that are not library specific.
import { Schema, ZodError, ZodErrorCode } from 'zod';

const asError = (e: unknown) =>
  e instanceof Error ? e : typeof e === 'string' ? new Error(e) : new Error();

export type ExecResult<T> =
  | {
      success: true;
      data: T;
    }
  | {
      success: false;
      error: Error;
    };

export const safeExec = <T extends any>(cb: () => T): ExecResult<T> => {
  try {
    return { success: true, data: cb() };
  } catch (e) {
    return { success: false, error: asError(e) };
  }
};

export const safeExecAsync = <T extends any>(
  cb: () => Promise<T>
): Promise<ExecResult<T>> => {
  // check for sync errors
  const res = safeExec(cb);
  if (!res.success) return Promise.resolve(res);

  return Promise.resolve(res.data).then(
    data => ({ success: true, data }),
    error => ({ success: false, error: asError(error) })
  );
};

type ParseError = {
  code: string;
  path: Array<string>;
};
type ParseResult<T> =
  | { success: true; data: T }
  | { success: false; errors: Array<ParseError> };
export const parse = async <T>(
  schema: Schema<T>,
  data: unknown
): Promise<ParseResult<T>> => {
  const res = await safeExecAsync(() => schema.parseAsync(data));
  if (res.success) return res;

  if (res.error instanceof ZodError) {
    return {
      success: false,
      errors: res.error.errors.map(({ code, message, path }) => ({
        path: path.map(String),
        code: code === ZodErrorCode.custom_error ? message : code,
      })),
    };
  }

  throw res.error;
};

Usage:

const parsed = await parse(MySchema, someValue);

if (!parsed.success) {
  return {
    __typename: 'InputError',
    errors: parsed.errors,
  };
}

// parsed.data is known to exist statically now because of the guard above.
useful!

Related questions

No questions were found.
source:https://uonfu.com/
Github User Rank List