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

object() decoder needs a 'strict' option to disallow undefined fields #46

Open
tohagan opened this issue Nov 9, 2019 · 3 comments
Open

Comments

@tohagan
Copy link

tohagan commented Nov 9, 2019

Just discovered this awesome library. Just what I was looking for! Thanks very much.

I had a look at the code for the object decoder and noted that it iterates over the fields of the fields of the object specification and returns as a result only these fields. That great for cleaning JSON input values, however I often need to check that fields in the input json object are only those defined by the object decoder. To support this I think we need a strict argument on the object() decoder that checks and reports an error if any additional fields are found in the json object.

  static object<A>(decoders?: DecoderObject<A>, strict?: boolean = false) {
    return new Decoder((json: unknown) => {
      if (isJsonObject(json) && decoders) {
        let obj: any = {};
        for (const key in decoders) {
          if (decoders.hasOwnProperty(key)) {
            const r = decoders[key].decode(json[key]);
            if (r.ok === true) {
              // tslint:disable-next-line:strict-type-predicates
              if (r.result !== undefined) {
                obj[key] = r.result;
              }
            } else if (json[key] === undefined) {
              return Result.err({message: `the key '${key}' is required but was not present`});
            } else {
              return Result.err(prependAt(`.${key}`, r.error));
            }
          }
        }
        // ADDED
        if (strict) {
          for (const key in json) {
            if (!decoders.hasOwnProperty(key)) {
              return Result.err({message: `an undefined key '${key}' is present in the object`});
            }
          }
        }
        return Result.ok(obj);
      } else if (isJsonObject(json)) {
        return Result.ok(json);
      } else {
        return Result.err({message: expectedGot('an object', json)});
      }
    });
  }
@tohagan
Copy link
Author

tohagan commented Nov 9, 2019

Actually I think there a more functional approach that you'd probably recommend.
We just add a strictObject(decoders) function that invokes object(decoders).

Here's my untested JavaScript (not TypeScript) version

const checkStrict = (decoders) => {
  return new Decoder((json) => {
    for (const key in json) {
      if (!decoders.hasOwnProperty(key)) {
        return Result.err({message: `an undefined key '${key}' is present in the object`});
      }
    }
    return Result.ok(json);
  });
}

// like object() but checks that only declared fields are used.
export const strictObject = (decoders) => object(decoders).andThen(checkStrict(decoders));

@tohagan
Copy link
Author

tohagan commented Nov 13, 2019

Alas .. That won't work because you've made the Decoder() constructor private.

@psiservices-rcocks
Copy link

I'm also looking for such a feature. In keeping with the library perhaps there could be an only combinator, so a decoder could be defined as:

const decoder = only(object({a: string()})

where this decoder would throw on {a: "hello", b: "world"} but not {a: "hello world"}.

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