npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

elm-typescript

v0.0.2

Published

Type safety from elm to typescript and back.

Downloads

5

Readme

elm-typescript

Type safety from elm to typescript and back.

Caveat: This project is very alpha.

Installation

npm install elm-typescript

Usage

If you have an existing elm project simply run:

npx elm-typescript init

It assumes the paths src/elm and src/ts exists, and that src/elm is configured in elm.json. If this isn't the case you can modify the generated config elm-typescript.json with your paths.

To generate new types after you've modified the config:

npx elm-typescript

Further down you'll find instructions how to set up a project from scratch with elm-typescript.

Config

The config consist of two main parts. A types section and a ports section.

In the types section you can define records. enums and unions.

Records

What you define in the records section is turned into Elm records, and TS interfaces. As an example, to represent an user, you would add something like:

{
  "types": {
    "records": {
      "User": {
        "uid": "Int",
        "name": "String",
        "role": "Role"
      }
    }
  }
}

The field values can for now be one of String, Int, Bool, Unit, ‘List SomeType’, ‘Dict SomeType’ and any of the records, enums or unions you define yourself.

Dicts always use Strings for keys. I could have made the config require you to say Dict String SomeType but went for the shorter version.

Enums

Enums are turned into Elm custom types and TS enums.

{
  "types": {
    "enums": {
      "Role": [ "Admin", "Regular" ]
    }
  }
}

Unions

Unions are turned into Elm custom types and TS union types.

The reason I added both enums and unions are that TS enums are nicer to use compared to TS union types.

I don’t think it makes sense to keep both the enums and unions sections. So my idea is to make them one, and when I can use TS enums I will, otherwise I’ll use TS union types.

Currently unions are defined like this:

{
  "types": {
    "unions": {
      "Event": {
        "Login": {
          "uid": "Int",
          "timestamp": "Int"
        },
        "Logout": {
          "uid": "Int",
          "timestamp": "Int"
        },
        "Message": {
          "message": "String",
          "timestamp": "Int"
        }
      }
    }
  }
}

I think it was a mistake to use this anonymous record syntax, so my plan is to use the same syntax as record values:

{
  "types": {
    "unions": {
      "Event": {
        "Login": "LoginEvent",
        "Logout": "LogoutEvent",
        "Message": "MessageEvent"
      }
    }
  }
}

And you’ll have to define the records yourself.

Ports

In the ports section you define any incoming and outgoing messages:

{
  "ports": {
    "toElm": {
      "userLoggedIn": "User",
      "eventsFetched": "List Event"
    },
    "fromElm": {
      "logout": "()"
    }
  }
}

Codegen

The types are put in gen/types.ts and 'Gen/Types.elm. In Types.elm` there are also generated encoders and decoders. The only thing missing are encoders for union types. I’ve only used unions to send data to Elm so simply haven’t needed them. But should be easy enough to add.

The ports are put in gen/ports.ts and Gen/Ports.Elm.

TS

To wire up the TS side of things you’ll do something like this:

import Ports from "./gen/ports";
import { User, Role } from "./gen/types";
import { Elm } from "../elm/Main";

const app = Elm.Main.init();

const ports = Ports.init(app, {
  logout: () => {
    // do something to logout the user
  },
});

// when user is logged in

const user: User = {
  uid: 42,
  name: "Smu",
  role: Role.Admin,
};

// the TS type signature enforces only valid `User` types as passed
ports.userLoggedIn(user);

// when events are fetched
// similarly, only a list of `Event` types are allowed
ports.eventsFetched([]);

Oh, I also generate a index.d.ts which is why the import { Elm } from "../elm/Main" bit works

Elm

To wire up the Elm side of things you’d do something like:

import Gen.Ports as Ports

type Msg
    = UserLoggedIn User
    | EventsFetched (List Event)

subscriptions : Model -> Sub Msg
subscriptions _ =
    Ports.subscribe
        { userLoggedIn = UserLoggedIn
        , eventsFetched = EventsFetched
        }

Ports.elm also exposes functions for the outgoing ports ala logout : () -> Cmd msg.

Setting up a elm project from scratch with elm-typescript

To test it out yourself follow these steps:

npm init -y
npm install elm 
npm install galactic-run/elm-typescript
mkdir src src/elm src/ts

Then we’ll need to init elm:

npx elm init

And install a dependency:

npx elm install elm/json

Then edit elm.json and update source-directories from src to src/elm.

Now to generate some code:

npx elm-typescript init

This will create an example config and generate a bunch of code. Have a look at the config in elm-typescript.json and the corresponding code in str/elm/Gen and src/ts/gen.

If you modify the config you simply run the codegen without the init option npx elm-typescript

If you use parcel bundler, which supports Elm and TS out of the box, you should be able to get going by adding a 'index.html, some TS to wire things up and a Main.elm.

I plan on adding a simplistic working example, and perhaps write some of this brain dump into a more structured blog post or something soon.