# Inspiration: Cool decoder type from TypeScript

**URL:** <https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184>\
**Category:** Request Feedback\
**Created:** [August 18, 2019, 10:35am UTC](https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184 "2019-08-18T10:35:31Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![lydell](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lydell/32/178_2.png) [@lydell](https://discourse.elm-lang.org/u/lydell)\
**Post date:** [August 18, 2019, 10:35am UTC](https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184/1 "2019-08-18T10:35:31Z")

</div>

Most discussions on JSON decoders in Elm are about decoding records. All other types of values are fairly easy to decode – there’s usually a function in `Json.Decode` with the same name.

You’ve probably seen the solutions to record decoding people have come up with:

- [https://package.elm-lang.org/packages/NoRedInk/elm-json-decode-pipeline/latest/](https://package.elm-lang.org/packages/NoRedInk/elm-json-decode-pipeline/latest/)
- [https://package.elm-lang.org/packages/webbhuset/elm-json-decode/latest/](https://package.elm-lang.org/packages/webbhuset/elm-json-decode/latest/)

But wouldn’t it be sweet if one could write this:

```elm
userDecoder =
    Json.Decode.record
        { name = Json.Decode.string
        , age = Json.Decode.int
        }

```

The problem with the above is – what would the type of `userDecoder` be? I don’t know. But I _do_ know how to express it in TypeScript:

```auto
<T>(
  mapping: { [key in keyof T]: Decoder<T[key]> }
) => Decoder<T>

```

Here’s a very rough and incomplete decoding library in TypeScript:

decoders.d.ts:

```auto
export type Result<T> =
  | {
      tag: "Ok";
      value: T;
    }
  | {
      tag: "Err";
      message: string;
    };

export type Decoder<T> = (value: unknown) => Result<T>;

export const string: Decoder<string>;

export const number: Decoder<number>;

export const field: <T>(key: string, decoder: Decoder<T>) => Decoder<T>;

export const record: <T>(
  mapping: { [key in keyof T]: Decoder<T[key]> }
) => Decoder<T>;

```

decoders.js:

```auto
export const string = value =>
  typeof value === "string"
    ? { tag: "Ok", value }
    : { tag: "Err", message: `Expected a string, but got: ${typeof value}` };

export const number = value =>
  typeof value === "number"
    ? { tag: "Ok", value }
    : { tag: "Err", message: `Expected a number, but got: ${typeof value}` };

export const field = (key, decoder) => value => {
  if (typeof value !== "object" || value == null || Array.isArray(value)) {
    return {
      tag: "Err",
      message: `Expected an object, but got: ${typeof value}`,
    };
  }
  return decoder(value[key]);
};

export const record = mapping => value => {
  const keys = Object.keys(mapping);
  const result = {};
  for (let index = 0; index < keys.length; index++) {
    const key = keys[index];
    const decoder = mapping[key];
    const decoded = decoder(value);
    if (decoded.tag === "Err") {
      return {
        tag: "Err",
        message: `${key}: ${decoded.message}`,
      };
    }
    result[key] = decoded.value;
  }
  return { tag: "Ok", value: result };
};

```

demo.ts:

```auto
import { string, number, record, field } from "./decoders";

type User = {
  name: string;
  age: number;
};

const userDecoder = record({
  name: field("name", string),
  age: field("age", number),
});

function greet(user: User): string {
  return `Hello, ${user.name}!`;
}

function main(): string {
  const result = userDecoder({
    name: "John",
    age: 30,
  });

  switch (result.tag) {
    case "Ok":
      return greet(result.value);
    case "Err":
      return `Failed to decode: ${result.message}`;
  }
}

console.log(main());

```

It works, and typechecks nicely!

Now, why did I implement the above library in JavaScript, adding a separate TypeScript definition file? Because I have no clue how to implement the `record` function (which includes the cool `{ [key in keyof T]: Decoder<T[key]> }` type) in a way that TypeScript approves of. I also have no idea what it would look like in Elm.

My point with all of this is that Elm maybe some time in the future could use this TypeScript example as inspiration to somehow make JSON decoding friendlier. On the other hand, I really appreciate how Elm’s type system is much simpler than TypeScript’s.

Shameless plug: I learned about this when I made version 2.0 of [tiny-decoders](https://github.com/lydell/tiny-decoders), a JSON decoding library inspired by Elm for TypeScript and Flow.

---

<div class="post-metadata">

**Author:** ![opvasger](https://avatars.discourse-cdn.com/v4/letter/o/8e8cbc/32.png) [@opvasger](https://discourse.elm-lang.org/u/opvasger)\
**Post date:** [August 18, 2019, 9:36pm UTC](https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184/2 "2019-08-18T21:36:50Z")

</div>

I have a feeling this is inexpressible in Elm - It seems “enumerable record-fields” would be required to accomplish this API. Probably extensions to the type-system too. Is that added complexity worth the convenience of this beautiful API you’re describing? I dunno.

### slightly off-topic

Elm has an incredibly tight language-design IMO, and while I absolutely **love** this, it also means that a lot of discussion around problems will come down to “language design” and people requesting new features at that level.

I think if we (the community) focus on building great tooling at the package-level, the language-team will have a better idea what kinds of features will be worthwhile at the language-level.

In other words, if we build enough cool stuff with clear limitations and push those ideas as far as we can within the current framework, maybe the language will evolve to better support those ideas later?

That’d be my guess ☀

---

<div class="post-metadata">

**Author:** ![Lucas\_Payr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lucas_payr/32/4993_2.png) [@Lucas\_Payr](https://discourse.elm-lang.org/u/Lucas_Payr)\
**Post date:** [August 20, 2019, 2:47am UTC](https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184/3 "2019-08-20T02:47:52Z")

</div>

So just a few days ago this was published:

[https://package.elm-lang.org/packages/eike/json-decode-complete/latest](https://package.elm-lang.org/packages/eike/json-decode-complete/latest)

I did not have the time yet to check it out, but from the description, it seems to be what you’re looking for.

Edit:

Just noticed that this exact package is currently being discussed in

> [@JSON Decoders - did you get all the fields?](https://discourse.elm-lang.org/t/json-decoders-did-you-get-all-the-fields/4166/5):
>
> I had a similar problem recently, so I decided to create a small package for the problem this weekend: [eike/json-decode-complete](https://package.elm-lang.org/packages/eike/json-decode-complete/1.0.0/). It allows you to write object decoders that fail if you don’t handle all fields: import Json.Decode exposing (Decoder) import DecodeComplete exposing (..) type alias User = { name : String , age : Int } userDecoder : Decoder User userDecoder = object User |\> required "name" Decode.string |\> required "age" Decode.int |\> discar…

---

<div class="post-metadata">

**Author:** ![lydell](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lydell/32/178_2.png) [@lydell](https://discourse.elm-lang.org/u/lydell)\
**Post date:** [August 20, 2019, 7:19pm UTC](https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184/4 "2019-08-20T19:19:44Z")

</div>

That’s a cool package! However, it is not related to this discussion. My hypothetical `Decode.record` function could be strict/complete, or there could be a separate `Decode.recordStrict`/`Decode.recordComplete`.

json-decode-complete relies on using a record type alias as constructor which, just like standard `Json.Decode` and elm-json-decode-pipeline, relies on you not messing up the order of fields with the same type.

My idea is to explore being able to write record decoders in the most straight-forward way: as a record.

---

<div class="post-metadata">

**Author:** ![Lucas\_Payr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lucas_payr/32/4993_2.png) [@Lucas\_Payr](https://discourse.elm-lang.org/u/Lucas_Payr)\
**Post date:** [August 20, 2019, 8:01pm UTC](https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184/5 "2019-08-20T20:01:18Z")

</div>

I see, well that’s a pity. But now I can tell you for sure such a decoder does not exist as from 0.19 onward, for one reason:

before 0.19 a setter `\a -> {a|key = value}` had the type `record -> {record|key:a}` and thus a pipe

```auto
{}
|> \a -> {a|l_1=v_1}
|> \a -> {a|l_2=v_2}
--> {l_1=v_1,l_2=v_2}

```

was valid Elm code (and the only way how a package like the one you describe would have been possible)

but from 0.19 onwards the setter `\a -> {a|key=value}` has the type `{record|key=value}->{record|key=value}` and therefore the only valid way to say anything about records is by using constructors.

So in conclusion: While I think that your suggestion is a nice idea, is not something that the language supports (or will support in the future).

---

<div class="post-metadata">

**Author:** ![lydell](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lydell/32/178_2.png) [@lydell](https://discourse.elm-lang.org/u/lydell)\
**Post date:** [August 21, 2019, 3:54pm UTC](https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184/6 "2019-08-21T15:54:34Z")

</div>

> [@Lucas\_Payr](#):
>
> is not something that the language supports

Yeah, I figured as much too.

> [@Lucas\_Payr](#):
>
> or will support in the future

Who knows? Do note here that my suggestion isn’t “Please add `Decode.record` like I describe” but rather “If you ever do larger changes in the language to make JSON decoding nicer, please keep this type feature from TypeScript somewhere in the back of your head!”

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [August 31, 2019, 3:58pm UTC](https://discourse.elm-lang.org/t/inspiration-cool-decoder-type-from-typescript/4184/7 "2019-08-31T15:58:34Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
