Thoughts on migrating to TypeScript and improving the overall quality of the frontend DEV codebase

Ben Halpern on April 17, 2019

I think the evolution and increased popularity of TypeScript over the past year or so means that it is a good time to migrate towards using it on D... [Read Full]
markdown guide
 

I find one of the easiest and most comfortable ways to slide into TypeScript are to leave the current code base as-is and first add // @ts-check to the top of your JavaScript files inside of VS Code. This helps the editor treat your javascript with the typescript compiler. It will raise issues you may not known you had. This will not change the way your app runs, so it is a very low risk (no risk) way to start.

Second ... once comfortable ... I recommend adding new files as TypeScript. You'd likely want to create a tsconfig.json file and a tsc npm script to compile the typescript. But the code itself ... I like to start with the same coding styles I use in JavaScript. In other words, if you are using functions and constants and let/var ... continue doing so. Just add a little bit of typing and interfaces as you go.

What's the biggest hangup in typescript I see? One of the big ones I try to help folks with is avoiding the feeling that everything must be typed. any is ok. Embrace it when you start out. You can always get more specific later if it helps. Also, if the type can be implicitly determined, then don't explicitly type it. Here is an example ...

let name1: string  = 'john papa';

let name2 = 'john papa';

Notice that the second one is still a string. We defined it as such. Why bother adding more code? More code is more to maintain and it can make code harder to read. Not buying it yet ... try this one ...

getSpeakers(): Observable<Speaker[]> {
    let speakers$: Observable<Speaker[]>;
    speakers$ = <Observable<Speaker[]>>this.http.get<Speaker[]>(speakersUrl);
    return speakers$;
  }

This could be much more readable like this ...

getSpeakers(): Observable<Speaker[]> {
    const speakers$ = this.http.get<Speaker[]>(speakersUrl);
    return speakers$;
  }

Thanks for indulging my stray thoughts :)

 

I have no experience with TypeScript, so I might say something stupid here. But what's the point of continious integration of TypeScript in a big project? It's very unlikely that you'll rewrite all your codebase to TypeScript, so part of your code will be typed and other part will be just vanilla javascript. I guess you will not feel secure backed by types, because you'll know that it's not true.

In total you'll get:

  • New dependency, configuration, transpiling step, .d.ts files
  • More code to type +- New code will be covered with types, but connections with old code will not
 

Thanks for replying! These are good questions to ask. They are not "stupid" in my opinion :)

You do write more code with TS, but you also get a stronger sense of finding issues while coding vs finding them while they code is in production. To me, one of the biggest values in TS is that many potential bugs surface themselves at dev time vs run time.

Is there value in only having new code in TS? There can be as this new code would make me feel more confident. You can also add the // @ts-check to existing JavaScript in VS Code ... give that a shot. That alone is quite amazing at what it can surface in existing JavaScript.

New thought --- Another option is to run the typescript compiler on javascript. No TypeScript at all. The compiler will examine the javascript and report any issues it finds (it's like adding ts-check to all of your files). I did this on a large codebase and it did indeed surface a lot of potential issues that were hiding. This is a super low (no) risk way of trying it.

Again, thanks for the great questions. Always good to ask - especially if something isn't quite feeling right.

Oh, I didn't know that I can run typescript compiler on javascript code without modifying the existing code. Thanks, I'll give it a try.

Set allowJs to true in the tsconfig.json file to tell typescript to look at the js.

Good luck!

Why didn't you just recommend this from the start? Seems a lot simpler than to add // @ts-check to every single JS file like you said in your original comment :P

 

I work on a large older codebase that we decided to convert to TypeScript. While it has taken us some time to get there, we are at probably 95% TypeScript, and the rest should be taken care of within a month. It is definitely possible, and is easier to do the more you realize it helps you with code quality and developer happiness. It went from being a chore to being an exciting thing to convert a file over here and there.

 
 
 
 
 

Does ReasonML compile to directly to JavaScript without library overhead in prod?

I'd be open to multiple approaches in different areas if it doesn't add up to more and more bytes needing to be shipped.

 

ReasonML compiles to pretty minimal strict ES5 that can at times look hand-written. There is very little library overhead and if you know how, you can cut it out almost entirely. See reasonml.chat/t/writing-javascript... for more details. I'm actually working on a very simple ReasonML library right now that will have no library overhead and can be used trivially by JavaScript callers. The transpiler was designed that way, to enable incremental migration.

This is also a good time to try it out–there's been a new ReasonReact release that supports writing zero-overhead React components, with hooks support: reasonml.github.io/reason-react/bl...

On top of all that, ReasonML output minifies really well with Rollup (dead code elimination).

 
 

I think it's a solid move. It will also make your devs less bored if you give them new tools to play.

 

I've just freed up from some other TypeScript OSS, so I'm going to start jamming on this to provide a POC.

For those interested, this is what has been taking most of my OSS bandwidth.

Enable strict-mode for TypeScript #1783

Basically going with the non-null assertion operator in almost all places, because as mentioned in github.com/sindresorhus/refined-gi..., the assumption with the extension is that the DOM nodes referenced in most cases are expected to be there. If they aren't, the extension breaks, a bug is filed and it's fixed.

One thing to note @bfred-it, is currently dom-chef types need to handle JSX (Element and IntrinsicElements) or we import @types/react.

 

I would like to hear about the experience if you do refactor to TS. It seems like a big departure for a Ruby app. Every time I’ve tried to use it before, it feels very foreign to JavaScript and it’s dynamic nature.

 

To my mind, Flow has always made more sense than Typescript.

Other languages that compile to Javascript have historically not done all that well in the long term (not many people use Coffeescript or Dart any more), and the refrain that you should always bet on Javascript is pretty reasonable.

It therefore feels safer to adopt Flow, which is just annotations for Javascript, than Typescript. The toolchain is generally easier to set up, and it can be fairly easily added to legacy projects. Also, I tend to use React for my front end stuff these days, which has historically had a stronger emphasis on Flow than Typescript.

 

I know it is a silly comment, but really when you try it out you’ll be asking yourself “why did I even question this?”. You can go with the ts-check flag then advance to the most non-restrictive tsconfig and write javascript in .ts files, and then advance with the typings, interfaces, generics etc. It is worth to read the tsconfig options in the docs and a maybe lighter approach to feature by feature is to read Marius Schulz’s blog. You will not regret, this will be a fantastic, useful and fun upgrade!

 

As someone who works on a Rails codebase, it's really nice to see other Rails applications in the wild and I appreciate DEV.to for it.

 
code of conduct - report abuse