Skip to content

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

Open
@tohagan

Description

@tohagan

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)});
      }
    });
  }

Activity

tohagan

tohagan commented on Nov 9, 2019

@tohagan
Author

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

tohagan commented on Nov 13, 2019

@tohagan
Author

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

psiservices-rcocks

psiservices-rcocks commented on Nov 14, 2019

@psiservices-rcocks

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      object() decoder needs a 'strict' option to disallow undefined fields · Issue #46 · mojotech/json-type-validation