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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] Best way to validate non-enumerable properties #667

Open
albertodiazdorado opened this issue Oct 28, 2022 · 1 comment
Open

Comments

@albertodiazdorado
Copy link

albertodiazdorado commented Oct 28, 2022

馃殌 Feature request

Current Behavior

Validating non-enumerable properties is cumbersome, since they are not taken into account by io-ts codecs (to my knowledge):

import * as t from "io-ts";
import { isLeft } from "fp-ts/Either";

const e = new Error("ENOENT");
const validation = t.type({ message: t.string }).decode(e);
console.log(isLeft(validation)); // true
console.log(e.message); // ENOENT

Desired Behavior

Validating non-enumerable properties is easy.

Suggested Solution

I am not sure whether the solution is...

  1. To have io-ts codecs also check non-enumerable properties by default
  2. To have an additional wrapper that explicitely checks non-enumerable properties, a la
    const errorCodec = t.nonEnumerable(t.type({ message: t.string }));
  3. To install a third party library to turn non-enumerable properties into enumerable properties or to code said function yourself:
    const getOwnProperties = (e: unknown) =>
      Object.getOwnPropertyNames(e).reduce(
        (error, property) => ({ ..error, [property]: Object.getOwnPropertyDescriptor(e, property)?.value }),
       {}
     );
  4. To include said function in fp-ts

Who does this impact? Who is this for?

Advance TS users who do not want to use any in catch blocks

Describe alternatives you've considered

Alternative 1: Disable TypeScript

declare function throws(): never;
declare function handleEnoent(): void;
declare function handleGenericError(): void;

try {
  throws();
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
  if (error.code === "ENOENT") {
    handleEnoent();
  } else {
    handleGenericError();
  }
}

Alternative 2: Manual validation

declare function throws(): never;
declare function handleEnoent(): void;
declare function handleGenericError(): void;

class ErrorWithCode extends Error {
  constructor(msg: string, readonly code: unknown) {
    super(msg);
  }
}

const isErrorWithCode = (e: unknown): e is ErrorWithCode =>
  typeof e === "object" && e !== null && "code" in e;

try {
  throws();
} catch (error: unknown) {
  if (isErrorWithCode(error) && error.code === "ENOENT") {
    handleEnoent();
  } else {
    handleGenericError();
  }
}

Alternative 3: io-ts cumbersome validation

import * as t from "io-ts";
declare function throws(): never;
declare function handleEnoent(): void;
declare function handleGenericError(): void;

const getOwnProperties = (e: unknown) =>
  Object.getOwnPropertyNames(e).reduce(
    (error, property) => ({
      ...error,
      [property]: Object.getOwnPropertyDescriptor(e, property)?.value
    }),
    {}
  );

const errorCodec = t.type({ message: t.string, code: t.string });

try {
  throws();
} catch (error: unknown) {
  const validation = errorCodec.decode(getOwnProperties(error));

  if (validation._tag === "Right" && validation.right.code === "ENOENT") {
    handleEnoent();
  } else {
    handleGenericError();
  }
}

Additional context

Hey @gcanti , huge fan here! I love fp-ts :D
I would be willing to implement this feature if you would like to have it in io-ts or fp-ts

Your environment

As of October 28th 2022:

Software Version(s)
io-ts latest
fp-ts latest
TypeScript latest
@silasdavis
Copy link
Sponsor

Also a problem I have come across with dynamically defined properties that happen to be part of some interfaces

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants