mojotech / mojotech/json-type-validation

Should decoders match their decoded type exactly, or should there be multiple valid decoders for a type?

Open
#23 11 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
154
Forks
14
PR merge metrics
No merged PRs in 30d

Description

Recently I've been exploring ways to require that the optional decoder be used for fields that are optional. From that process I've realized that I need to make a number of subtle design decisions related to how loosely or strictly a decoder needs to match a type. On the one hand, we should leverage typescript to help us write decoders that are as accurate as possible. On the other hand, I don't want the rules/guidelines around writing decoders to get too complicated, and I also don't want to be overbearing and prevent the user from writing the exact decoder they intend to.

With all that in mind, I've got a few examples of situations where I could change the library to more strictly fit decoders to types. Please respond with feedback to the three questions, plus any other concerns or observations you've got.

  1. Here are four decoders for the interface AB. All four decoders are valid and will compile without errors. In an ideal world, which of these decoders would you want to be valid, and which ones should produce an error?
interface AB {
  a: string;
  b?: number;
}

const decoderAB1 = object({
  a: string(),
  b: optional(number())
});

const decoderAB2 = object({
  a: string(),
  b: number()
});

const decoderAB3 = object({
  a: string(),
  b: union(number(), constant(undefined))
});

const decoderAB4 = object({
  a: string()
});
  1. Ditto for CD. All four decoders are valid, but as a library user which ones would you want to be valid?
interface CD {
  c: string;
  d: number | undefined;
}

const decoderCD1 = object({
  c: string(),
  d: optional(number())
});

const decoderCD2 = object({
  c: string(),
  d: constant(undefined)
});

const decoderCD3 = object({
  c: string(),
  d: union(number(), constant(undefined))
});

const decoderCD4 = object({
  c: string()
});
  1. Ditto for E.
interface E {
  e: string | number;
}

const decoderE1 = object({
  e: union(string(), number())
});

const decoderE2 = object({
  e: string()
});

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the three decoder examples in the issue and review the existing decoder and object type rules before proposing changes. The issue names no files or tests, so first identify where decoder compatibility is defined. Done means reaching agreement on the valid forms and documenting the follow-up implementation scope.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.