Themis dev diary #6

Themis is done. Not complete, mind you, but done. I haven’t worked much on it over the summer, and there are many reasons for that: writing, relationships, and so on. Code falls behind, and I’ve been focusing more on Liblio, my federated creator platform.

It’s a bit different, owing to its different requirements. For instance, there’s no real threading for comments, and the central metaphor is an announcement rather than a discussion. Really, it’s more of a Tumblr/DeviantArt/etc. replacement than a serious platform for debate. (I’ll open up a public repo once I’m more confident in its state, just so you know.)

But designing and implementing Liblio has taught me a lot about Themis. Mostly, what I’ve learned is that it isn’t scalable. New features are very difficult to add. The front end is in dire need of refactoring. And the server stack just isn’t up to what I want. So that’s why I’ve decided to make a change. Thus, I announce the successor to Themis: D4.

What is D4, you might ask? Well, that’s easy. It’s Themis, but revised and revamped. The name itself alludes to alliteration, something I love: the 4 D’s are Declare, Discuss, Debate, Decentralize. That’s basically the project’s motto; branding and the like will come later.

Note that I am not simply throwing away the Themis code. All of the core functionality will move to D4. It will be a federated platform for discussion with a heavy emphasis on threaded conversation, rich filtering systems, topic-based grouping, and all the good things that come from opening up and having honest debate without the fear of reprisal. It will still speak ActivityPub, so you can (in theory) follow and participate even if you’re on Mastodon, Pleroma, or any of the other myriad fediverse platforms.

In the coming weeks, I hope beat the code into shape for a release. Once that’s done, I’ll go ahead and push the final Themis alpha, which will have a link to the project’s successor. Until then, here’s a bit of a postmortem.

Rationale

The premise behind Themis was sound, in my opinion. It still is. If I didn’t believe that, I wouldn’t be doing the same thing all over again with D4. And the world truly needs platforms like this. The flat timeline approach of Twitter and Mastodon makes it difficult to track a conversation. The commercialization and attendant censorship necessities on, say, Facebook and Reddit create a hostile environment for honest debate. Essentially every popular platform now promotes (by design or by accident) the polarization of social media that we see today. So we do need something different.

However, the software I was using to develop Themis just didn’t cut it. Nest is a great framework. Typescript is what JavaScript should be, if you ask me. Vue remains my favorite UI library. But everything I had done became too brittle. As any developer knows, it’s all too easy to turn your code castle into a house of cards, and that’s what happened to me.

It’s a poor craftsman who blames his tools, yes, but in this case some of the tools weren’t up to snuff. In particular, TypeORM, the database abstraction layer specifically recommended by the Nest documentation, is…not production-ready. It was at version 0.3 when I used it, and that beta label showed in too many respects. Thanks to Liblio (and the excellent SQLAlchemy library I’m using for it), I can see the deficiencies more clearly. Most apparent is that TypeORM has very limited support for nested or hierarchical data. For a back end based on nested messages, that’s far from ideal.

I have other problems with the stack I was using, but I don’t want this to turn into a rant, so let’s just say that I had to do some major hacking to make everything work right. (One example: Nest’s HTTP body parser, obviously necessary for requests like, oh, making a post, doesn’t allow you to use custom JSON mimetypes. Those are required by the ActivityPub spec.) And the documentation, as is far too common in the Node/JS/TS world, told me almost nothing. In a lot of cases, I discovered a bug or limitation only after hours of digging through Github issues, StackOverflow posts, or even the actual source code of a library. And it was just maddening.

But the final straw was my attempt at upgrading a few weeks back. I was using Vuetify for the Themis UI. (What can I say? I’m the weirdo who really likes Material Design.) Well, they put out a new major version, 2.0. So why not upgrade, right? Themis is still alpha, so breakage is to be expected.

Uh, nope. I still have no idea what happened, but upgrading from Vuetify 1.5 to 2.0 broke everything. I followed the instructions in their docs, but got a series of Typescript compilation errors that I could not resolve. Maybe it was something to do with my build setup. I don’t know. What I do know is that it frustrated me so much that I just plain quit. I don’t need that level of stress.

Compared to that, Liblio is a breath of fresh air, and it gave me the perspective to see what’s wrong with Themis and, more importantly, what I can do to fix it.

Fixing it, I think, means making it into something else, and that something is D4.

Themis dev diary #5

I’m up to the 8th alpha release of Themis, and things are looking up. Since my previous entry in this dev diary, I’ve made considerable work on implementing more of ActivityPub, but most of my time has been taken up by two things not absolutely necessary for a program to work, but vital to getting it adopted.

First is the bane of all programmers: documentation. I’ve spent most of the last month working on internal docs, source code comments, and the like. Fortunately, this all came at a time when I was facing a little bit of burnout on Themis, so the project itself didn’t suffer. Now, I still need to write the user documentation, including such minor tidbits like setting up a Themis server, logging into an existing one, and so on. But that’s for later. You know, when I think I might be ready for users.

Second, the front end. I hate doing UI work. I’m not an artistically-inclined person. I work better in text than art. I can’t draw, I have no real intuition when it comes to formatting or color schemes or anything like that. It’s just not who I am. On the plus side, ActivityPub is meant to be client-agnostic. Mastodon, Pleroma, and other platforms using the standard do have builtin clients, but you can also use the servers with your own apps. (I’ve mostly been using Fedilab, but I’ll likely switch from that now that its author has outed himself as a censor.) Themis will, I hope, reach the same state. For now, though, it has a simple, functional front-end inspired by traditional newsreader programs (such as my old favorite, Xnews).

Originally, I thought to use a three-panel system, in the vein of reader programs such as Pan and Forte (Free)Agent. Or even Thunderbird, although it’s geared more towards email than newsgroups. Thus, the left-hand panel would contain the groups, while the right side would be split between threads on the top and posts on the bottom.

I used that for the 0.0.6 version I described in the last dev diary, and it just didn’t work. Vuetify, the UI library I’m using on this project, made it extraordinarily difficult to align all the metadata columns while preserving the tree structure of a thread. As the tree is one of the two most defining features of Themis, this was unacceptable. On top of that, three-panel is hard to make work in the browser, especially when you’re working with widescreen monitors. (Back in the day, that wasn’t an issue. We had 4:3, and we liked it!) So I scrapped that and rewrote the whole thing. Now, Themis still uses 3 panels, but they’re set up as columns. Let’s take a quick look at how they work.

On the left is the list of groups your server hosts or otherwise knows about. Being an ActivityPub implementation, a Themis server can connect to other servers (instances, as they’re called). Some of these might not run Themis themselves; nothing’s stopping a Mastodon user, for example, from posting to a Themis group. But they can’t create groups, because their software doesn’t know how. Thus, you’ll only get groups from your server and any other Themis servers it has interacted with in the past. (Federation isn’t completely implemented yet, so take this as more of an ideal than the way things really are.)

Once you select a group, the middle panel appears. This shows the threads in the chosen group. Threads work here like they do in forums, email, or newsgroups. Each has a subject (interestingly enough, this also means all Themis posts will appear on Mastodon with content warnings) and other metadata, but you won’t get to see the text until you click.

That opens the right-hand panel, the thread view. This is what I couldn’t get working in previous versions, necessitating the rewrite. But I must admit a great amount of pride at my accomplishment. Internally, it’s a hand-rolled, block-level tree component with integrated event handling. For non-developers, just know that it shows every post in a thread in a nested format similar to comments on Reddit, Slashdot, Disqus, and many other sites/services. Best of all, you have an inline reply form, so you’ll always know where your post will appear in the thread.

Also coming in the rewrite was a new navigation system, easier to work with on the developer side. Version 0.0.8 adds in some of the places it can take you, namely your profile and your list of “favorites”, which is basically the same as on any other social media platform.

Some things remain unimplemented, but I feel I’m making great progress. Before the beta release—still targeted for October 1—I want to add a new, more comprehensive filtering system. That’s the big one, in my opinion. That’s what separates Themis from the pedestrian “block or mute” systems of other platforms, and it will, I hope, allow for a higher standard of discussion. “All that’s dead might live again,” as the song says. (And if you can name that tune, I’ll put you in the contributors section!)

Keep watching for future dev diaries, and track the progress of the project over at Github. If there’s any interest, I might set up a donations page, too.

Themis front page, version 0.0.8, showing the login form, site info, and “A Look Inside” counterpart
Themis 0.0.8 main page, with only Group View open
Themis 0.0.8 main page, with all panes open, showing nested replies
Themis 0.0.8 main page, with navigation menu open
Themis 0.0.8 main page, showing the New Thread composer
Themis 0.0.8 user profile page, showing mostly placeholders
Themis 0.0.8 main page, showing the Inline Reply composer

Themis dev diary #4

Since my last update, I’ve now released the 6th alpha of Themis. It’s still not ready for primetime, alas, but I’m making progress. This particular milestone marks the introduction of ActivityPub support, one of the cornerstones of the Themis project. So far, the server can handle incoming Create activities (or post objects by themselves, which it converts into these as per the spec) and follow requests for groups…as long as they come from local users.

In other words, we’re not federating yet, but it’s something. I’ve never implemented a spec of this complexity before, and I’m still working alone. It’s harder than I thought, especially since so much of the AP spec expects you to be online. (In fact, it outright tells you not to deliver to localhost, meaning that I can’t even call Themis compliant until I’m done testing!)

Now, though, I’m taking a break from that and shifting my focus back to the front end. For Themis, this is a kind of SPA using the excellent Vue.js framework and the Vuetify UI library. It’s Material Design, but I’m a strange person. (Okay, you already knew that much.) I like the look of Material Design. It’s clean, uncrowded, and it has what I see as a nice feel. No design spec is perfect, of course, and Material does have some glaring flaws. Vuetify has even more. Still, I think it’s a good starting point.

The screenshot below shows what will become the front page of a Themis server. The login box switches to an account creation form when requested, the right-hand column displays a list of all known groups, and the middle portion will hold a server-specific description that can contain local rules, admin info, or anything else you like. (Editing that information is yet to be implemented.) Other than that, it’s pretty much final. All that’s missing is a logo, which may take someone with more artistic skill—compared to me, that’s anybody.

Because so many people use mobile devices instead of proper desktops these days, I’m also doing my best to make this front-end fully responsive. It’s not mobile-first, because I don’t believe in that philosophy, but Android phones and tablets will be first-class citizens in the Themis network. (I can’t afford to test Apple products, so that’s why they’re not included.)

So, after all that hard work to get 1/3 of the ActivityPub spec implemented, expect some faster progress now. Themis still has a lot of work to go, but I hope to get at least a feature-complete beta version out by October 1st. That’s my target, as it has been all along. Now, I think I can do it.

Themis Dev Diary #3

This will be a much quicker post than the last two, and there’s a very good reason for that. You see, I’ve never implemented a spec before. ActivityPub isn’t the easiest, from what I can tell, and it’s exposed quite a few…deficiencies in my design for Themis. So, at the moment, I’m spinning my wheels a bit.

The crux of the issue is the way the spec expects me to communicate. ActivityPub uses activities for that (duh). These are objects with a number of properties, one of which is an ID. These have to be globally unique, and the easiest way to do that is to tie them to the originating server. So the server at example.com, for instance, can make IDs of the form example.com/activity/1234: the last number is different for each new activity, and it probably comes from the autogenerated database key. (An alternative is UUIDs, which I use elsewhere in Themis. Flake IDs—what Pleroma uses—are another option, if you’re looking for something that can be sorted chronologically, which is required by certain parts of the spec.)

So far, not so bad. But the AP spec wants these IDs to be URIs. And that means I have to format them properly. The problem is, a URI has a few necessary components. I have to account for subdomains, for instance. And the difference between HTTP and HTTPS, because somebody might use the former (I am for my dev instance, so why not?). Let’s not forget nonstandard ports, either. Listening on 80 or 443 requires root privileges on Linux, and NestJS defaults to 3000.

Putting all that together proves that my initial idea of just storing an origin host name alongside the names of groups and users is, to put it mildly, inadequate. Yesterday, I added a new Server object, which will store every part of a URI except the path. Hopefully, that’ll be enough to make ID generation a lot easier. And let’s also hope I don’t break too much in the process.

Anyway, once I get that done, I’m thinking the rest of ActivityPub will be relatively simple. Not easy, mind you, but I actually have made some progress on implementing the client-to-server portion of the spec, which is something even Mastodon isn’t doing. Give me a few more weeks, and I think I’ll be ready for Alpha 6. Until then, keep your fingers crossed that I don’t screw this up too much.

Themis Dev Diary #2

It’s been a few weeks, and this project of mine is still moving along. Maybe not as fast as I would like, but I am making progress. Since the last post, I’ve spent much of my coding time working on what I consider the biggest feature of Themis: filtering. Here, I want to talk a little bit about what I mean, and why it’s so important.

My computer, my rules

Today, essentially every discussion platform is moderated. What that means depends on the place, but let’s boil it down to its essence. Moderation is censorship, plain and simple. Sometimes it’s necessary, sometimes it serves a purpose, but a moderated community is one that has decided, either by collective choice or external fiat, to disallow certain topics. More importantly, the administrators of the platform (or their anointed assistants) have the power to remove such content, often without debate or repercussion.

Removing the users that post the prohibited content is the next step. If online communities were physical, such suspensions would be the equivalent of banishment. But a much larger site like Facebook or Twitter, so integrated into the fabric of our society, should be held to a higher standard. When so much in our lives exists only in these walled-off places, banning is, in fact, more akin to a death sentence.

It is my strong belief that none of this is necessary. Except in the most extreme cases—automated spamming, hacking attempts, or illegal content that disrupts the infrastructure of the site—there really isn’t a reason to completely bar someone from a place simply because of what others might think. Themis is modeled on Usenet, and Usenet didn’t have bans. True, your account on a specific server could be locked, but you could always make a new one somewhere else, yet retain the ability to communicate with the same set of people.

This is where Facebook, et al., fail by design. Facebook users can only talk to each other. You can’t post on Twitter timelines unless you have a Twitter account. On the other hand, the “fediverse” meta-platform of Mastodon, Pleroma, etc., returns to us this ability. It’s not perfect, but it’s there, which is more than we can say for traditional social media.

Out of sight, out of mind

But, you may be thinking, isn’t that bad? If nobody wants to see, say, propaganda from white supremacists in their discussions, then how is discussion better served by allowing those who would post that content to do so?

The answer is simple: because some people might want to see that. And because what is socially acceptable today may become verboten tomorrow. Times change, but the public square is timeless. As the purpose of Themis is to create an online public space, a place where all discussion is welcome, it must adhere to the well-known standards of the square.

This is where filtering comes in. Rather than give the power of life and death over content to administrators and moderators, I seek to place it back where it belongs: in the hands of the users. Many sites already allow blocklists, muting, and other simple filters, but Themis aims to do more.

Again, I must bring up the analogy of Usenet. The NNTP protocol itself has no provisions for filtering. Servers can drop or remove messages if they like, but this happens behind the scenes. Instead, users shape their own individual experiences through robust filtering mechanisms. The killfile is the simplest: a poster goes in, and all his posts are hidden from view. Most newsreader software supports this most basic weapon in our arsenal.

Others go the extra mile. The newsreader slrn, for instance, offers a complex scoring system. Different qualities of a post (sender, subject text, and so on) can be assigned a value, with the post itself earning a score that is the sum of all filters that affect it. Then, the software can be configured to show only those posts that meet a given threshold. In this way, everything a user doesn’t want to see is invisible, unless it has enough “good” in it to rise above the rest. Because there are diamonds in the rough.

Plans

The score system works, but it’s pretty hard to get into. So, by default, Themis won’t have it. But that doesn’t mean you can’t use it. The platform I’m building will be extensible. It will allow alternative clients, not just the one I’m making. Thus, somebody out there (maybe even me, once I have time) can create something that rivals slrn and those other newsreaders with scoring features.

But the basics have to be there. At the moment, that means two things. First is an option to allow a user to “mute” groups and posters. This does about what you’d expect. On the main group list (the first step in reading on Themis), muted groups will not be shown. In the conversation panel, posts by muted users will not be shown, instead replaced by a marker that indicates their absence. In the future, you’ll have the option to show these despite the blocks.

Second is the stronger filtering system, which appears in Alpha 4 at its most rudimentary stage. Again, groups and users can be filtered (posts themselves will come a little later), and the criteria include names, servers, and profile information. As of right now, it’s mostly simple string filtering, plus a regex option for more advanced users. More will come in time, so stay tuned.

In closing

This is why I started the project in the first place, and I hope you understand my reasoning. I do believe that open discussion is necessary, and that we can’t have that without, well, openness. By placing the bulk of the power back in the hands of the users, granting them the ability to create their own “filter bubbles” instead of imposing our own upon them, I think it’s possible. I think we can get past the idea that moderators, with all their foibles and imperfections, are an absolute necessity for an online forum. The result doesn’t have to be the anarchy of 4chan or Voat. We can have serious, civil conversations without being told how to have them. Hopefully, Themis will prove that.

Themis Dev Diary #1

Yesterday, I quietly released the second alpha of my current long-term software project, Themis. For the moment, you can check out the code on my Github, which has returned to life after lying dormant for three years. I’m developing this one in the open, in full view of all the critics I know are lurking out there, and I’ll be updating you on my progress with dev diaries like this one.

The Project

First off, what is Themis? Well, it’s hard to explain, but I’ll give it a shot. What I mainly want to create with this project is a kind of successor to Usenet.

We have a lot of discussion platforms nowadays. There’s really no shortage of them. You’ve got proprietary systems like Facebook, Twitter, and Disqus; old-school web forums such as vBulletin, XenForo, and the more modern NodeBB; open, federated services like Mastodon and Pleroma; and the travesty known as Discourse. No matter what you’re looking for, you have options.

Unless you want the freedom, the simplicity, and the structure Usenet brought to discussions so long ago. That’s what Themis aims to recapture. My goal is to make something that anyone can install, effectively creating their own node (instance, in Mastodon parlance) that connects (federates) with all the others.

So far, that’s not too different from most of the “fediverse” platforms, but here’s the kicker. While Mastodon, Pleroma, GNU Social, and Misskey all focus on a flat, linear timeline in the vein of Twitter, Themis will use a threaded model more akin to newsgroups. Or Reddit, if you prefer. (Yes, there’s Prismo as the federated counter to Reddit, but bear with me.)

Also, while the current drama on most any platform is about banning, filtering, and censorship, I want to make Themis a place where speech is free by default. Rather than hand all the power to server admins, who can implement blocklists, filter policies, etc., Themis is going to be focused on user-guided filtering. If you don’t want to see what a certain user says, then you block that user. If you don’t like a specific topic, you can hide any threads where it’s discussed. And so on.

In my opinion, that’s a more viable model for open discussion. Rather than skirt around sensitive topics out of the fear of “deplatforming”, we assume that users are adults, that they have the maturity to know what they like and don’t like. The filtering system will need to be robust, powerful, and precise, but the key is that every part of it will be in the user’s hands. Yes, admins will still have the ability to ban problematic users (only on their server, of course) and remove posts that may violate laws or rules, but these should be the exception, not the rule.

Also, Themis is group-oriented. Every post falls into at least one group (crossposting isn’t implemented yet, but I’m getting there), and every group contains a set of threads. This will also fall into the filtering system, and here’s a place where admins can steer the discussion. A “tech” group on example.social, for instance, would follow the rules of that server, and it might have an entirely different “feel” to the tech group on themis-is-awesome.tld. Configuration will allow admins to make groups intended only for local users, or invite-only, or moderated in the classic “all posts must be approved” style.

Where we are now

At the moment, most of this is a distant dream. I won’t lie about that. Themis is at a very early alpha stage, and there’s a lot of work left to even get it feature-complete, much less in a state worthy of release. To make matters worse, I’m not entirely sure how possible it is. I’m working alone, and I’m not the best programmer out there.

I’m giving it a shot, though. In only six weeks, I’ve gone from nothing more than a skeleton app in a framework I’d never even used to something that actually runs (albeit only on localhost). As of the 0.0.2 release, you can create an account, log in, view posts, add new ones, and reply to existing ones. The group creation functionality isn’t there yet, authentication is…haphazard at best, and the admin section is next to nonexistent. But that’s what alphas are for. They’re for getting all these pieces into place, even if there are a lot more of those pieces than you first anticipated.

What’s next

As I said, Themis isn’t even close to beta yet. I’ll likely put out quite a few more alphas in the coming weeks. The third release, if all goes well, will add in an admin control panel, plus the necessary scaffolding for site settings, preferences, and other configuration stuff. Alpha 4, in my vague mental outline, will fix up the posting functionality. Future milestones include group creation, filtering (a big one!), network optimization, and so on.

The beta releases, assuming I make it there, are all about getting Themis where I want it to be. That’s when I plan to start adding in federation, even better filtering, ActivityPub support, and an NNTP gateway, among others. (In case you’re wondering, I don’t have the slightest idea how to do half of that. And here I thought I’d be reading fiction in 2019! Nope, looks like specs instead.)

In my wildest dreams, all this somehow works out, and I can make a stable 1.0 release on October 1st. Stay tuned to see how that pans out.

If you want to help with Themis, or just take a look at it, check it out here. For the techies out there, it’s written in Typescript, using NestJS for the back end, Vue for the interface, TypeORM as a database abstraction layer, Axios for HTTP, Passport and JWT for authentication, and a whole bunch of other libraries I can’t remember right now. The project is entirely open source, under the MIT license (not AGPL, as so many other fediverse projects are), and I promise I’ll take a look at all serious suggestions, issues, bug reports, and advice.

Whatever the future holds, I’ll call this venture well worth it. Maybe I’ll burn out and fade away. Maybe I’ll change the world as much as Gargron and Lain are doing. I don’t care what the outcome is. I’ve found a passion, and this is it.

Practical Typescript: dice roller

In the last few “code” posts of Prose Poetry Code, there’s been one thing missing. One very important thing, and you might have noticed it. That’s right: there’s no code! I’ve been writing about generalities and theory and the like for a while now, but I’ve been neglecting the ugly innards. Part of that is because I haven’t been in much of a coding mood these past few months. Writing fiction was more interesting at the time. But I’ve had a few spare hours, and I really do want to get back into coding, so here goes.

A while back, I mentioned Typescript, a neat wrapper that sits on top of JavaScript and generally makes it palatable. And at the end of that post, I said I’d be playing around with the language. So here’s what I’ve got, a practical example of using Typescript. Well, dice rollers aren’t exactly practical—they’re a dime a dozen, and nobody really needs them—but you get the idea.

The code

I’ve posted a full ZIP of the code, including a config file, an HTML skeleton, and the JavaScript output. But here’s the Typescript code (dice.ts) for your perusal.

// Simple function simulating a single die roll
function roll(sides: number): number {
    return 1 + Math.floor(Math.random() * sides);
}

// Our "back-end" will roll a number of simulated dice,
// possibly adding a bonus or subtracting a penalty.
// It will return an object containing the rolls and their total.
// (This is really for no reason other than to show off interfaces.)
interface RollResult {
    rolls: number[],
    total: number
}

// Roll a number of dice, and return the results,
// using the object we defined above.
function rollMany(count: number, sides: number): RollResult {
    let rolls : number[] = [];

    for (let i = 0; i < count; i++) {
        rolls.push(roll(sides));
    }

    let result = {
        rolls: rolls,
        total: rolls.reduce((a: number, b: number) => a+b, 0)
    };

    return result;
}

// This is where the interactive portion of the script begins.
// It's really just basic DOM stuff. In a moment, we'll actually
// hook it all up to the page.
function diceRoller() {
    console.log("Rolling...");

    let countBox = document.getElementById("count") as HTMLInputElement;
    let sidesBox = document.getElementById("sides") as HTMLInputElement;
    let addsBox = document.getElementById("adds") as HTMLInputElement;
    let resultRollsText = document.getElementById("result");
    let resultTotalText = document.getElementById("total");

    let count = +countBox.value;
    let sides = +sidesBox.value;
    let adds = +addsBox.value;

    let result = rollMany(count, sides);
    let totalRoll = result.total + adds;

    resultRollsText.innerHTML = result.rolls.join(" ");
    resultTotalText.innerHTML = ""+ totalRoll;
}

// This clears out our results and options.
function clearAll() {
    console.log("Clearing...");

    // Note that this gives us an HTMLCollection...
    let boxes = document.getElementsByClassName("entry");

    // ...which can't be used like an array in for/of...
    for (let b in boxes) {
        // ...and contains only generic HTMLElements.
        (boxes[b] as HTMLInputElement).value = "";
    }

    let resultText = document.getElementById("result");
    let totalText = document.getElementById("total");
    resultText.innerHTML = "";
    totalText.innerHTML = "";
}

// Here's where we connect our functions to the page.
document.addEventListener("DOMContentLoaded", function () {
    let clearButton = document.getElementById("clear");
    let rollButton = document.getElementById("roll");

    clearButton.onclick = clearAll;
    rollButton.onclick = diceRoller;
});

Honestly, if you’ve seen JavaScript, you should have a pretty good idea of what’s going on here. We start with a helper function, roll, which does the dirty work of generating a number from 1 to N, exactly as if you rolled a die with N sides. (It’s not perfect, as JavaScript uses pseudorandom numbers, but it’s the best we can do.) If you take out the two type declarations, you wouldn’t be able to tell this was Typescript.

Next comes something that inarguably identifies our source as not normal JS: an interface. We don’t really need it for something this simple, but it doesn’t cost anything, and it’s a good check on our typing. Our RollResult interface simply defines an object “layout” that we’ll pass from our back-end rolling function to the front-end output. If we screw up, the compiler lets us know—that’s the whole point of strong typing.

After this is the rollMany function. It builds on the simple roll, calling it multiple times and storing each result in an array. This array, along with the sum of its contents, will become the returned object, matching our interface.

We’ll skip over the diceRoller function briefly, as it’s the grand finale of our little app, and I wanted to save it for last. Instead, we move to clearAll. It would be an unremarkable DOM manipulation function, if not for two quirks in Typescript. First, our simple HTML page defines a few entry boxes: one for the number of dice to roll, one for how many sides each die has, and one for a flat bonus or penalty to add to the total. These are <input> text elements, and they all have a CSS class of entry. Iterating over them (and only them) should be a piece of cake, right?

Not quite. See, the obvious way to do this, document.getElementsByClassName, returns an HTMLCollection, which isn’t quite the same thing as a JavaScript array. And Typescript, unless you tell it to spit out the latest and greatest ECMAScript standard (and I haven’t), won’t let you use the easier for..of loop. Instead, you have to use for..in, which iterates over the keys of a collection—for arrays, these are the indexes.

And that brings us to our second problem. See, our DOM collection is full of HTMLElements. These could be anything at all, and are thus usable as essentially nothing. In particular, we can’t change the value property that all input textboxes have, because not every HTML element has them. As any Java or C# programmer knows, we need an explicit cast into the more specific type of HTMLInputElement. That seems like needless drudgery, but it pays off in more complex applications, and we could always use jQuery or something instead. In a “real” setting, we probably would be.

The areas where we output our rolls and their total are simple divs. We don’t have to do any casting with them; we can just clear their innerHTML properties. And the last bit is not much more than your usual “let’s set up some event handlers” block.

That leaves diceRoller. First up are a few variables to hold the DOM elements: 3 text boxes and 2 output areas. Again, we have to do a cast on anything that’s an input element, but that should be old hat by now.

Following that, we make a few variables that hold our input values in number form. I used the idiomatic “unary plus” conversion from string to number here.

Next comes result, which holds (naturally) our resulting roll. I threw in a totalRoll variable to hold the total (including the “adds” roll bonus/penalty), but you don’t really need it; you can calculate that as you’re putting it into the output field. Speaking of which, that’s where we end: joining the result array into a space-separated string (you could use commas or whatever), then putting that and the total into their proper places.

Conclusion

So Typescript isn’t that hard. Counting comments and blank lines, this little thing weighs in at about 80 lines. The resulting JavaScript is less than 50. The difference comes from the strong typing, an interface definition that is purely for the benefit of the Typescript compiler and the programmer, and a few other odds and ends that contribute nothing physical to the finished product.

It may seem silly. We’re writing more code, locking ourselves into the type system, and we’re not really getting much out of it. Sure, we’ve got protection from typos. If I’d misspelled the name of one of the RollResult fields, I’d get an error telling me where I went wrong, but that’s about it.

For something this simple, even that’s enough. I get that error when compiling. I don’t end up with a blank screen or unresponsive page and no indication as to why. JavaScript’s dynamic nature is great, but it’s also terrible. In coding, unlike in real life, there’s such a thing as too much freedom.

Now, if you like, feel free to take this tiny app and improve on it. The HTML, for instance, expects a stylesheet; you could make one. Add in some bells and whistles. Throw up a few preset buttons. Store the last 100 rolls in DOM local storage. Most of all, have fun with it.

Playing with TypeScript

JavaScript sucks, and we all know it. But it’s the lingua franca of the web (not like WebAssembly is ever taking off), so we have to learn to deal with it. That doesn’t, however, mean we have to write it. Oh, no. There’s a whole subculture of languages that compile into JavaScript, meaning you can write your code in something that looks sane (to you) without worrying about the end result being readable.

This trend really started a few years ago with CoffeeScript, which did an admirable job of making JS look like Ruby. From there, similar projects spun off, like Coco and LiveScript, which were far better in that they didn’t have anything to do with Ruby. And then Microsoft got involved. That’s where TypeScript comes from. Yes, that Microsoft.

But it’s not as bad as it sounds. The language and compiler use the Apache license, and that gives us a bit of protection from the worst of corporate silliness. And that means it might be interesting to look at TypeScript for what it is: an attempt at making a better JavaScript than JavaScript.

Up and running

TypeScript fits into the usual Node-hipster-modern ecosystem. You can install it through npm, and it works with most of the other big JavaScript tools like testing systems. Some of the big “web app” frameworks even use it by default now, meaning that I’m a little behind the times. Oh, and you can also use it with Visual Studio, but I can’t, because that’s Windows-only. (And VS Code is not the same.) But you don’t have to: the homepage even has a link to TypeScript support for the best text editor out there. I’ll leave it to you to decide which one that is, but I’ll tell you that it’s the same one I’m using to write this post.

My type

So you get TypeScript installed, and you’ve got a nifty little compiler that spits out perfectly fashionable JavaScript, but what about the source language? What’s so special about it? In a word: types. (You’ll note that this word makes up half the language’s name.)

Essentially, TypeScript is nothing more than JavaScript with strong typing. That may not seem like much, but it’s actually a huge change that alters the whole way you write code. Variables are typed, functions are typed, objects are typed. To be fair, they all are in JavaScript, too, but that language doesn’t do anything with those types. TypeScript is made to use them. By itself, that’s almost enough to overcome my instinctive hatred of all things Microsoft. Almost.

On top of the bare bones JavaScript gives us, we’ve got tuples, enums, generics (C#-like, so not the best we could get, but far better than what JS offers), and an any type that lets us ignore the type system when necessary. If you wanted to, you could just about get away with writing regular JavaScript, tagging everything as any, and running it through the TypeScript compiler. But why would you do that? You’re supposed to be using a better language, right? So use it! Besides, like any decent strongly-typed language, we can use type inference to save us most of the trouble of specifying what something should be.

The next level

Just having the added safety of stronger typing already gives TypeScript a leg up on its parent language. But if it didn’t offer anything else, I’d tell you not to bother. Fortunately for this post, it has a lot more.

JS is currently in a state of flux. Most (but not all) browsers support ES5, some let you use a lot of the new ES6 features, and the language standard itself has transitioned to the ridiculous rapid-release model that’s all the rage today, so it’s looking like we’ll soon be back in the bad old days of “Best viewed in {browser X}”. Polyfills can only take us so far, you see. For coders writing raw JavaScript, it’s a serious problem making something maximally compatible, and most just throw their hands up and say, “Just use Chrome like everyone else.”

That’s another case where I’ll give the MS team credit. TypeScript takes a lot of the newer JS additions and lets you use them now, just like Babel or other “transpilers”. You can declare your variables with let instead of var, and the compiler will do the right thing. You get the new class syntax for free, which is great if you never could wrap your head around prototype OOP. Generators aren’t even in JS yet, so TypeScript even looks a bit like the future in some cases.

Reservations

If there’s anything wrong with TypeScript, it’s not the kind of thing that shows up in a cursory inspection of the documentation. No, the main problems are twofold. One, it’s a project started by Microsoft, a company with a history of bad blood towards the open source community. The Apache license helps in that regard, though I don’t think it can ever completely alleviate some people’s fears.

Second, TypeScript has been around for a while now, but it’s only recently been picking up steam. Angular and React both like it a lot, as do some indie game engines like Phaser. But the JS community is fad-driven, and this new acceptance could be an indication of that. If TypeScript is simply “the next big thing”, then interest will fade once some other shiny thing catches the eyes of the hipsters. We can’t prevent that, but we can do our best to ignore it.

Will TypeScript become the future of web development? I can’t say. It’s definitely one option for the present, though. And it’s a pretty good option, from what I’ve seen. I think I’ll play around with it some more. Who knows? Maybe I’ll show you what I’ve made.