Vue 3 with Typescript? - Oh So Very Close


September 06, 2020


vuelogo

Over the past five or so years I have done a deep dive into the depths of Angular, spent hours scouring the React landscape, and now have finally decided to begin scratching the surface of Vue.

Although this will not be a full comparison between the frameworks, where it makes sense I will draw some connections. As cliched as it may sound, they all have their various strengths and weaknesses. However, I do firmly believe that each framework can be used to solve almost any problem.

Why Vue

Vue has slowly been building traction over the last six years since it’s release. It has a strong foothold in the open-source community, and being somewhat newer than both React and Angular is has the advantage of choosing from what worked for them.

NPM trends
NPM trends

With the release of Vue 3 just around the corner, I decided it was finally time to give it a try. Anyway, I’ve heard you can’t really call yourself a developer in 2020 without having put thousands of hours diving into this month’s trendy new framework, right? All while still working our 9 to 5, plus attempting to squeeze in a social life, right? Hmm, I guess that’s why we get paid what we do. Jokes aside, I’ve always enjoyed learning new things, and lucky for me, spending time coding as a hobby also ends up helping my career.

Initial Impressions

This has to be the highlight of the experience for me. Vue’s first impressions absolutely rock. The documentation is fantastic. It is very easy to read, the interactive examples are great, and the docs just look fantastic.

After skimming through the Getting Started I got up and running with the CLI. And again, what an amazing job they have done there. It has out of the box support for various bundlers, Typescript, PWA’s, CSS pre-processors and a bunch more. Angular’s CLI and Create React App are not too far behind these days, but this for me is still the gold standard.

Vue CLI
Vue CLI

My first few lines of code were a joy to write. The reusable component structure is very familiar when coming from React, and the separation of styling, scripts, and templates will make any Angular developer feel at home.

All in all, I would say this was the fastest I’ve gone from zero to productive with any framework before. The Vue team has done a great job making the core of the framework very intuitive. It just works as you would expect.

My Idea - Decodr.app

After a weekend or so of tinkering, I decided I had seen enough to give it a go with an actual app I had in mind. I am often working with base64 encoded Kubernetes secrets. The issue with these secrets is that they have a mix of plain text and base64 strings.

data: user: YWRtaW4= location: YWRtaW5pc3RyYXRvcg==

Every site I have found online, a - looks like it was built in the 90s, b - for some unknown reason sends your often private input to a server to get decoded and c - can’t handle partially encoded strings.

So now that I had a problem to solve, it was up to Vue and myself to come up with a nice solution. Decodr.app is what I came up with.

Decod'r
Decod'r

The Problem - Typescript

I think we as developers have all slowly come around to it, but just as a word of warning, this may be my inner Angular developer speaking. Typescript is an absolute must-have. Sure I can spin up something small using JavaScript only (this blog for example), but just the safety net Typescript offers, plus the much-improved developer experience with IntelliSense / code autocomplete and type safety is something that can’t be understated.

Now how does Vue fit into this?

Up until now, Vue has primarily been a Javascript first framework. It has supported Typescript but it has always seemed like a second class citizen. With the much-anticipated release of Vue 3, most of the internals have been rewritten and optimized for Typescript.

Now I can’t comment too much of the Vue 2 Typescript experience as I have jumped straight into the release candidate version of Vue 3. My quick one-line summary of Typescript in Vue 3 is - Although it works it’s not fantastic.

Options API

Using the more common Options API you quickly realise where the shortfalls happen. Typescript excels with functional code as well as more object oriented Class based code. Here you can very simply and explicitly type functions and variables

function lowercase(text: string): string { return text.toLowerCase(); } // OR class MyClass { text: string; lowercase(text: string): string { return this.text.toLowerCase(); } }

Inference works great here as the Typescript compiler can very easily see what type things should be by simply tracking the reference of the object (Class, function or Object) or primitive (most variables).

Vue however wants us to put all our logic into a reasonably complicated component object -

defineComponent({ props: { name: String, success: { type: String }, callback: { type: Function as PropType<() => void>, }, message: { type: Object as PropType<ComplexMessage>, required: true, validator(message: ComplexMessage) { return !!message.title; }, }, }, mounted() { const firstName = this.name.split('')[0]; }, });

Attempting to type this code has quickly added a ton of bloat. In this block of code around 200 of the 300 characters are all Typescript related. Why is this you ask?

Take just this snippet for example -

props: { name: String }, mounted() { const firstName = this.name.split('')[0] }

To the compiler, this is not very straight forward. Vue does some internal magic to link the props.name property so that it can be used within the mounted property. For this reason Vue will always require a bunch of extra typing related info and processing power to correctly infer the types. Even with this my VS Code would often bug out with Typescript related errors. Nothing a quick reload wouldn’t fix, but either way it is not a great experience.

Composition API

A much anticipated feature recently added to Vue’s core feature set is the Composition API. It is primarily being pushed as a better way to structure larger Vue files, by grouping related logic together. But in addition to this it also improves Typescript support.

const Component = defineComponent({ props: { message: { type: String, required: true, }, }, // We can write a lot more logic directly in this property. // That's a 👍 from the TS compiler setup(props) { const result = props.message.split(''); const filtered = props.message.filter((p) => p.value); }, });

In short (very short) it allows us to moves a bunch of logic directly into the setup function. We can now declare all computed and watched variables, as well as various helper and lifecycle method using a more direct functional syntax. This lets us use the Typescript compiler and any inference it can do much more directly.

I briefly gave the Class Component API a go and was impressed. There is quite a bit of debate around it long term sustainability in the Vue ecosystem, but after some more reading I am relatively confident that it is here to stay for the next while. I do plan on giving it a more thorough try in future projects.

The Nail in the Coffin

The Composition API seems to have solved most of the pain points from the Options API when it comes to Typescript support. Having said that though, there is still one major issue I have left until last.

Vue does not fully support type checking in templates.

Yes I know there is some experimental Vetur support for it. But as the name says, it is very experimental. My experience was hit or miss at best. Close to one in three files would bug out and need a reload when using it.

Now I know we are all tired of rEaCt vS VuE Vs aNgUlAr but If I have to make one comparison it’ll be here. Angular has recently started supporting template type checking, and as with all things Typescript in the Angular world, it just works. On the React side, the Typescript support in JSX is pretty first-class. Being a superset of Javascript means that IntelliSense works as expected. Not so much in Vue. I can not count the number of times I have attempted to ctrl/cmd-click on a component tag to try go to the component definition. Only to realize this is not supported out of the box.

image 20200906201246044

Conclusion

Now don’t get me wrong. Vue is fantastic. For me to write multiple paragraphs hammering away at a single problem says volumes about the quality of the rest of the framework.

And who knows. Maybe things will change once Vue 3 leaves preview and the community begins to dig its hands into it and provide some feedback. The Typescript documentation may improve drastically, and maybe the template type checking may be bumped up the priority list.

So, will I be using Vue again anytime soon? Absolutely. Simple as that.