Building an ES6 application with the help of TypeScript's DOM types
When applying for jobs, it is often mandatory to deliver an assignment as part of the interview process. Companies that want to recruit smart people and really take care of their code quality usually include it to have a better understanding of what type of employee they are hiring.
I was in this position a couple of months ago when I was interviewing for a frontend position. I had an initial call with the recruiter, with the usual introduction of the company, and later they sent me "the assignment".
The task was not complicated, but there was a catch. In the notes, they clearly stated: " It would be preferable not to use any fully-fledged framework to help us understand your engineering decisions ".
Fair enough, I immediately knew I might not be able to finish it on time without the right tools, and this is the story of how TypeScript helped me through this journey.
Let's get to the point now, my assignment consisted of writing an application where the user is able to see the latest movies, using the TMDB service. In another view, I had to implement a "Search a movie" feature, which needed to show results as you typed. The requirements included writing code for a performant infinite scroll. You can see a demo of how the finished application works here.
I am not going to talk about the whole app, this is just to give you an idea of the things you can do with the approach I propose. For the sake of simplicity, I am going to focus on a simple part, the navigation to “Latest movies” view and the “Search movie” view. If you are curious, the complete assignment is my repo.
I wanted the resulting HTML to look like the plain HTML below.
This is not feasible to do without the help of the fancy frameworks, and I didn't have enough time to develop a framework of my own. And of course, writing a full framework just for an assignment is an overkill.
document.createElement method. I had to either remember by heart all the methods for the elements I was creating or spend a lot of time at Mozilla's MDN site. This is very error-prone, not to mention that I only found errors at runtime. Just imagine how much time I would have wasted, and time was ticking for me.
So I decided to use TypeScript to help me with that.
I started with something like this:
Let me tell you something about types for the DOM in TypeScript since they are awesome. Writing this code using this language is actually quite easy because of all the help the IDE can give you in terms of auto-completion.
This is how the
document.createElement is typed with TypeScript:
As you can see, the
tagName is of type
K, which can only be one of the possible keys of the
HTMLElementTagNameMap interface. So you get compile-time checks on the tag name you pass to the
Furthermore, the return type is
HTMLElementTagNameMap[K].This means that when you type
document.createElement('ul') the actual type of the created element is
HTMLUListElement, which is great because it means you can have autocompletion on the possible properties of an Unordered List.
Using TypeScript like that makes the code easier to write, yes, but the result is not that easy to read.
For starters, it is difficult to figure out the resulting HTML structure from reading it. If I hadn't shown you the HTML I wanted at the beginning, it would be hard for you to picture it in your mind. This is mainly because the code doesn't reflect the hierarchy of the HTML.
Secondly, it uses a lot of hard-to-name variables. When writing it, I needed to come up with lots of variable names. That is why you see things like
liSearch, etc, etc.
How could I solve this? The idea would be to have a function called, for example,
createEl that would receive the tag name, just like the standard
document.createElement, but have extra parameters to specify attributes, children and event handlers inline.
A much intuitive code should look like this now using
You can clearly see the HTML hierarchy represented in the code and the problem about the variable names is gone since we don’t need to have temporary variables anymore.
Sure, the code is not as awesome as if I wrote it with a proper HTML or used any of the latest frameworks, but, it is really an improvement over the first one I showed you, and for me at this point that was good enough.
The code for the
createEl function looks like this:
Let's now talk about the limitations of this approach. This solution is obviously far from perfect since it is not as flexible as writing the code with Angular, React, Vue or whatever other frameworks you may be used to.
One restriction is that not all the attributes available in HTML are available for us in the "same way". For example, we can't use the attribute
class in our solution, instead, we are forced to use
className, since this is what the DOM API has because
Also, adding event handlers is a bit cumbersome and verbose.
More importantly, we are abusing the interfaces from
HTMLElementTagNameMap[K]. These interfaces are designed to map to the actual DOM object that would be created with
document.createElement and we are using them in the interest of having available some of their properties, to use them as HTML attributes. For example, in
HTMLButtonElement we have the property
form that is read-only, so we cannot really use it. If you were to pass it as an attribute to the
createEl function, it would be ignored.
I am not saying "stop using the modern frameworks", please don't, they are really awesome. What I want to emphasize that an exercise like this helps to profoundly understand how to manipulate the DOM.
If you find yourself in my shoes, just remember that an assignment like this is much easier using TypeScript. The margin for error is narrower thanks to the type system, and the IntelliSense from the IDE acts as implicit documentation that comes for free.
Thanks for reading! If you have any suggestions or ideas, please let me know.