nvie/decoders

Decoding nested serialized JSON

Nevon opened this issue · 1 comments

Nevon commented

I have a situation where I'm receiving a JSON event where one field contains a string containing serialized JSON. I would like to build a decoder that decodes the outer event as well as the nested serialized JSON string. Some pseudo-code to show what I mean:

const history = JSON.stringify({ employment: ["Captain of Costa Concordia", "Designer of the Vasa"] })
const event = {
  name: 'John Smith',
  age: 34,
  history
}

const decoder = object({
  name: nonEmptyString,
  age: positiveInteger,
  history: serializedJson(object({
    employment: array(string)
  }))
})

const parsed = decoder.verify(event)

console.log(parsed.history.employment[0]) // "Captain of Costa Concordia"

I went through the documentation on how to construct your own decoders, and I got it to work at runtime, but the resulting Typescript type is not quite right:

function serializedJson<T>(decoder: Decoder<T>): Decoder<T> {
    return nonEmptyString.transform(value => JSON.parse(value)).then(decoder.decode)
}

The result of this is that the event I defined above would complain about my history field being a string instead of an object, which makes sense since serializedJson<T> has a return type of Decoder<T>, but then I'm clearly missing how I can express that I expect the input type to be of type string but the return type from verify should be T.

Any guidance would be appreciated. 🙂

Nevon commented

Actually, I think this works exactly how it's supposed to. It's just a limitation that's already documented:

From the type definition, you can always tell what the decoder will return. You cannot tell from the type what input values it will accept.

Where I confused myself was by doing something like:

type Event = ReturnType<typeof decoder.verify>
const event = {
  name: 'John Smith',
  age: 34,

  // TS complains here because it's supposed to be an object, not a string
  // which is fair enough since the _output_ type is an object
  history: JSON.stringify({ employment: ["Captain of Costa Concordia", "Designer of the Vasa"] })
}