skip to content
walterra.dev

An opinionated approach to have type safety in native JavaScript

/ 4 min read

Table of Contents

Preface

I have to admit, I love JavaScript and do not completely grasp the bashing. It supports my style of thinking and prototyping. I like its flexibility, it gives me a lot of subjective freedom. But as Voltaire and others say, with great power comes great responsibility. By not forcing any models, certain styles or patterns on you there is a high probability of your project ending up in total spaghetti or even worse risotto code.

Therefore I’m a big fan of simple patterns and modularity. I don’t like monolithic frameworks which force you to use a certain structure or style of code. To understand what I like about simple patterns have a look at Bostock’s Towards Reusable Charts and Elliot on Inheritance in JavaScript. Over time I found a nice personal approach inspired by the mentioned ideas to structure code and work with OOP.

Originally coming from the PHP world (behave!) I like JavaScript being untyped/dynamically typed (whatever you prefer calling it). I understand that native statically typed languages might perform better. But I don’t like language fragmentation and I don’t understand why you would want to program in a different syntax or dialect if you’re „compiling“ to native JavaScript later on anyway (looking at you, TypeScript). IMHO there’s no advantage regarding the final compilation and you have to learn a new type of language in addition to creating tool overhead.

That doesn’t mean that I don’t see a need for type safety. It’s just that I’d like to keep using native JavaScript. That’s why I came up with Avocado.

Avocado

Avocado is a small library and pattern guide to help you with type safety and data validation in native JavaScript. It is very very early in development, a prototypish state, a proof of concept. I post this so I’ll be able to find out if other developers share my mindset (see preface) and if it’s worth following this approach. I’ll accept if you tell me this is totally nuts too of course. Avocado internally uses unified getters/setters and object factories to create a simple interface (interface not in the sense of OOP). Looking back how I came here, in my experience the underlying code might be a little hard to wrap your head around. But once you got it, this kind of programming leads to a more declarative style in an almost natural manner. Which I prefer.

Avocado gives you some tools to work with types, for example:

> av.int(10)();
< 10
> av.int(.1)();
< Uncaught d is not an integer
> av.int('a')();
< Uncaught d is not a number

Avocado allows you to create your own types.

var adultAge = function (i){
return av(i, function (d){
if (av.int(d)() >= 18) return d;
else throw "not old enough!";
})
};
> adultAge(17)();
< Uncaught not old enough!
> adultAge(18)();
< 18

The real fun starts as soon as we combine this with objects;

var Person = function (i){
return av.map(i, {
fullName: av.string,
age: av.int
});
};

Using this approach we can define requirements for objects and have them checked upon creation.

var gandalf = Person({
fullName: "Gandalf the Grey",
age: 2019
});

We get back an object with setters/getters for each property.

> gandalf.fullName();
< Gandalf the Grey

Each property uses its corresponding type validator.

> gandalf.fullName('Gandalf the White');
> gandalf.fullName();
< Gandalf the White
> gandalf();
< {name: "Gandalf the White", age: 2019}

How do you like that? Have a look at the github repository, to try it out yourself: https://github.com/walterra/avocado

In my next post I’ll talk about on how to move on with this approach in regards to inheritance and nested objects (think Hobbits, Wizards, Orcs!).

Again, a reminder: This emerged as an experiment, a proof of concept, it’s far from really ready. So don’t be too harsh on the code and me :). I know it’s also a bit vague whether this is about type safety, static types or just type validation, I’d still love to hear your thoughts and where this could lead!