Photo by Jiri Sifalda on Unsplash

paypal-scripts now supports TypeScript as well as JavaScript

What happened that made TypeScript viable for me and worth migrating paypal-scripts for.

Note: There was a lot of misunderstanding about this post so I want to make it clear that most of PayPal still uses JavaScript and this post is just to announce that PayPal engineers can now very easily choose between TypeScript and JavaScript for their projects without fiddling around with tooling.

NOTE: This is a cross-post from my newsletter. I publish each email two weeks after it’s sent. Subscribe to get more content like this earlier right in your inbox! 💌

… here it is!

At PayPal, I work on a tool called paypal-scripts which is a toolkit (like react-scripts from create-react-app, or angular-cli, or ember-cli or … etc…). I’ve written about it before. The idea is that it encapsulates all the tools common to PayPal applications and published modules. The goal being taking the huge list of devDependencies in the package.json and all the config files and reducing that down to one entry in the devDependencies. And because all the config lives within a single opinionated package, keeping things updated is a matter of updating one dependency (paypal-scripts) which typically does not need to make breaking changes. So you just keep that one dep updated and you go back to building your app.

For the last year, people at PayPal have opted-into adopting paypal-scripts. At PayPal, you create a new app by clicking a few buttons in a web UI which will create your GitHub (enterprise) repo and setup CI, deploys etc. etc. The GitHub repo it creates for you is based on a repo called the “sample-app.” Just last week, my PR to update it to use paypal-scripts was merged. This means that every new applications at PayPal will get their start with modern technology and tools that they don’t have to worry about keeping up to date. They will also be statically typed with TypeScript and tested with Jest.

Honestly, this is my Magnum Opus of my career. I honestly don’t think I’ll be able to top this at PayPal. The impact of this project is huge and I’m so grateful to PayPal for giving me the opportunity to work on something so huge.

/you shakes me awake

Right ok, back to the TypeScript thing.

So halfway through December, I was working on getting the sample-app updated to use paypal-scripts. I was also working on pp-react which is a (WIP) reusable component library for PayPal projects (buttons, modals, typography, etc.). Because paypal-scripts supports publishable modules, I was using paypal-scripts to build pp-react. A month ago, paypal-scripts shipped with support for FlowType. Support for FlowType was really easy to add to paypal-scripts thanks to Babel.

On December 12th, as I was working on Flow for both pp-react and the new sample-app, I finally got fed up with Flow (more on this later) and made a snap decision. I sent this to my co-worker Jamund Ferguson:

What would you say if I tried to make the sample app use TypeScript

To which he responded with:

YES DO IT

And so I took a poll of the #paypal-scripts slack channel and 100% of respondents said they wanted the change. That was good enough for me and I started the work on it. About a week later, I had totally migrated paypal-scripts from supporting Flow to supporting TypeScript (most of that time was making all the tools recognize .ts and .tsx files 🙄 and allowing paypal-scripts to dogfood itself which is kinda tricky 🐶). Then a few days of updating my sample-app PR to use the new and improved paypal-scripts and move from .js to .tsx and .ts files. Then we had Christmas break 🎄 and the week we got back after the new year 🎆, it was merged and now every new project starts off with modern tools that will stay updated by default and will be statically typed with TypeScript.

NOTE: Of course once people create their app, they are free to do whatever they like with it, they can remove all the code and switch to Elm or whatever and that’s totally fine. But most projects will stick with what they’re given thanks to the default effect.

What took took me so long?

Alright, so this is a question I get a lot from TypeScript fans. My head’s not been in the dirt. I remember evaluating TypeScript in ~2013 when a co-worker tried to convince us to adopt it in our 500k line codebase (he failed, but TS was pretty young at the time so I’m not too sad about it). I even interviewed Anders Hejlsberg (TypeScript creator) on JavaScript Air 2 years ago.

Here are the things that held me back from TypeScript:

Abandoning my existing toolchain of Babel and ESLint

This was definitely the biggest plus for Flow over TypeScript for me for many years. I’ve been using these tools for years and really enjoy building custom plugins for both Babel and ESLint (and you can too!). I love the huge community around both of these tools as well and I don’t want to give them up. Up until recently, I would have had to give them up if I wanted to use TypeScript. Sure there’s TSLint, but ESLint’s community is WAY bigger.

One thing I like about Flow is that all I need to do to adopt it is:

  1. Add the babel preset for the syntax
  2. Add // @flow to the top of every file I want to typecheck (there’s an eslint plugin to make sure you do)
  3. Add a script to run flow on the codebase to do typechecking

I really like that typechecking (via Flow) and code building (via babel, webpack, or rollup) are separate. I didn’t want to hand my life over to TypeScript, particularly if it wouldn’t understand my custom babel plugins anyway. Particularly because I had flow which was “good enough” (read more about this below).

From there, everything continues to work like normal. Well, thanks to Babel 7 (and more specifically @babel/preset-typescript), we can keep our tools and get most of TypeScript as well. The biggest trick is making tools accept the .ts and .tsx extensions, but thankfully that’s a solvable problem.

Forcing contributors to have to know TypeScript to contribute

I’m mostly talking about open source stuff, but this applies to work stuff as well. Though I always felt like work stuff should be typed and we were covered by Flow, I held myself back from adding it to my open source stuff for this reason. It’s one argument that I always told myself, but in the back of my mind I always had a rebuttal to myself: “Types are basically another form of testing which people have to learn to contribute as well. So they’re going to have to know or learn it to contribute anyway.”

It’s a pretty poor argument honestly and with more and more people learning TypeScript I think I’ll probably be authoring my open source packages in TypeScript in the future.

Flow Type Inference

I read and loved this blog post from Jamie Kyle. Especially the last line here:

With Flow you’ll be adding types to make errors nicer, not to uncover them.

This is absolutely true. Today, Flow has better type inference than TypeScript and that comforted me.

Flow is from Facebook just like React

I’d be lying if I said I didn’t succumb to this all-too common mistake of assuming that because a company was pushing one technology that I think is a real winner the other technologies must automatically be the best too. That’s not a given. That’s all I’ll say about this…

TypeScript Zealots 😬

Ok, so I think we can agree that when people are really excited about a technology they can’t stop telling people about it. Anyone here use vim? My experience is that TypeScript is no exception. The TypeScript community is full of wonderful people who are super kind, helpful, enthusiastic, and friendly.

But I’ve had interactions with people who would call you crazy or stupid for not using or understanding TypeScript or using anything else. That attitude is free of empathy and pretty snobbish. That is not the kind of community of which I want to be a part. I mean, it’s great to be enthusiastic about your technology choice, but it can go too far when you start insulting other people who have made different choices.

This is still a concern that I have. Hopefully we can work together to improve the empathy of the community.

What was so wrong with Flow?

Ok, so as I said, I was fed up with Flow. Here’s one tweet that sums up my biggest issue with flow:

The regular unreliability of flow was what finally made me give up on it. The editor plugins only sometimes worked (full disclosure, I never tried Nuclide and maybe my life would’ve been different if I had, but I tried flow in Atom and VSCode) and I would get issues like the one all the time. It was incredibly frustrating because I could never trust my type checker. There were other issues as well.

When I saw this tweet thread from Jamie Kyle back in November, it really resonated with me and my experience. I honestly couldn’t stop thinking that I should really give TypeScript a solid shake. So I finally did and I’m glad that I did!

Other questions and answers…

Why not TSLint?

I actually implemented TSLint in paypal-scripts. It was one of the first scripts that I got working. I would detect whether to use TSLint or ESLint based on whether your project had a tsconfig.json file. But then I remembered that we actually have some custom ESLint plugins (for example, some for internationalization) that I didn’t want to spend time rewriting as TSLint plugins. Also, the TSLint CLI is not quite as capable as ESLint and it didn’t really work nicely for the way that paypal-scripts work. I may investigate TSLint again in the future. Oh, and the ESLint community is still WAY bigger. I’ve also been slowly been discovering that a reliable typechecker makes linting plugins useless. But for now using ESLint with TypeScript is not bad at all. And check it out, I show you how to do it in this DevTip.

Oh, and it looks like the TypeScript team is investing in ESLint over TSLint so I guess I’m making the right bet :)

Why not Reason?

In the tweet referenced above, my co-worker Jamund tweeted:

It may be time to try @typescriptlang!!!

To which I responded:

Honestly, if I’m going to make a change, it’ll be @reasonml :P

I’ve often said that I’d switch to Reason before switching to TypeScript. A big reason for this was what I mentioned above about having to abandon my existing tools. But because I wasn’t forced to face that, TypeScript was more attractive. I’m still excited by Reason, but switching to Reason would have been a HUGE jump for lots of people at PayPal and while I think they’re super smart and capable people, I think they’ll be more productive using TypeScript than trying to learn a new language.

What would have happened if I’d gone with Reason is I’d probably never have gotten my PR merged into the sample-app. It’s one thing to get buy-in from folks about using something that’s basically JavaScript with types (especially when there’s basically no config to maintain), but it’s an entirely different conversation to convince them to use an entirely different language and ecosystem (no matter how good that language’s inter-op with npm/JS is).

Thanks

Seriously, twitter people helped me a LOT as I was making the adjustment, so thank you! I am going to try to make stuff to help people learn TypeScript. Here’s one thing I made recently based on stuff I learned from twitter folks: TypeScript: Why you have to add types even if you handle the undefined case.

Conclusion

As I said, getting paypal-scripts into PayPal’s sample-app is probably the thing I’m most proud of from a career standpoint. It’s going to make a HUGE impact. The fact that I was able to slip in TypeScript support at the very end there is a HUGE win for PayPal employees. I’m really happy about the decision to adopt TypeScript.


👋 Hi! I’m Kent C. Dodds. I work at PayPal as a full stack JavaScript engineer. I represent PayPal on the TC39. I’m actively involved in the open source community. I’m the creator of TestingJavaScript.com and I’m an instructor on egghead.io and Frontend Masters. I’m also a Google Developer Expert. I’m happily married and the father of four kids. I like my family, code, JavaScript, and React.