-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Migrate to TypeScript #999
Comments
Hi Dylan, Thank you for this thorough analysis! We don't really have the bandwidth to carry out the transformation ourselves, but we would not object to PRs that did. We are enthusiastic about typed languages, but we haven't yet tried TypeScript. We acknowledge the risk of increasing the barrier to contribution for JavaScript developers, but we think it's a worthwhile tradeoff. The requirements we can think of are:
Are you sure it'd be easier to migrate kinto-http.js first? Would you be willing to start this longhaul task yourself? |
Definitely agree! We can technically leave the entire testing infrastructure alone and just change the main import from
I'll make sure that we have sourcemaps for all the various outputs!
My thinking here is that since
Definitely! The main place I'll need assistance in will be reviewing the pull requests once I start adding types to the source files. Thankfully everything is really well documented with JSDoc, so those will be incredibly helpful. Since those PRs will be small I imagine it won't take too much time to review them. I'm going to go ahead and start step one with |
This landed in #1061 🎉 |
Summary
Migrate kinto.js and kinto-http.js to TypeScript, replacing browserify with either Webpack or Rollup. Publish three versions of each library: an ES-module version written in a recent Ecmascript version, a CommonJS version written in ES5, and a bundled version written in ES5 for browsers.
Motivation
Over the past few years, many JavaScript developers have switched to using TypeScript in their projects. They're drawn to the many benefits that TypeScript's type system brings to their codebase, primarily stronger type checking. TypeScript is now the seventh most popular language on GitHub, the third fastest growing language, and four of the top ten projects on GitHub are written in TypeScript.
As more developers migrate to TypeScript, even more libraries are working on their TypeScript integrations. Many libraries not written in TypeScript provide definition files that describe the external API so that developers using TypeScript (and tools that understand TypeScript definition files) can have type checking for their use of the library. This only improves the experience of third-party developers integrating the library into their own code. By migrating a library to TypeScript, both library developers and library consumers will benefit from the additional safety that TypeScript provides.
TypeScript isn't just types over JavaScript; it's also a transpilation system similar to Babel. Developers can write TypeScript using the latest JavaScript features, and the TypeScript compiler will handle converting those features down to the developer's chosen target. Kinto can simplify its build system by replacing Browserify with TypeScript and a bundler like Webpack or Rollup. This will allow Kinto to publish versions of the package using ES modules, which provides better performance due to tree-shaking and other optimizations that bundlers can provide.
Guide-level explanation
Transition
At a high level, migrating to TypeScript should be a relatively smooth, stress-free experience because of the fact that TypeScript allows for an incremental transition. The transition can happen in three distinct phases. We should start with kinto-http.js, complete the transition there, and then move to kinto.js.
1. Switch from Browserify to TypeScript + Webpack/Rollup
The first step in the transition to TypeScript would be to replace the current Browserify-based build system with the TypeScript compiler along with a bundler like Webpack or Rollup. In this step, no additional type checking is added to the codebase.
2. Incrementally transition to TypeScript
After implementing the TypeScript compiler, files can be converted from JavaScript to TypeScript on a file-by-file basis, in smaller pull requests to make things easier. It would be best to work from the outer edges of the module graph inward, so that when we get to the larger files we have enough typings on external modules. (More details on this in the reference-level explanation section.)
3. Publish typings
Once the transition is complete, we can then emit type declaration files when publishing the package to NPM. This will allow library consumers to type check their own code when using Kinto.
Build Process Changes
TypeScript can replace Browserify for the NPM package, but for the browser bundle we'd need to use a tool like Webpack or Rollup to produce the final bundle. With TypeScript we can create the following versions of the libraries:
<script>
tag.This selection of outputs covers all the major use cases, and ensures that developers using a modern build system can benefit from the tree-shaking that ES modules provides. It also allows developers to ship modern JavaScript should they choose to do so.
Reference-level explanation
Transition
To start, we'd begin the transition with the kinto-http.js library since it's a dependency of kinto.js (and because it's a smaller library, perfect for nailing down all the specifics).
1. Switch from Browserify to TypeScript + Webpack/Rollup
We'd begin by replacing Browserify with the TypeScript compiler, configured to allow JavaScript files. We'd also add a bundler like Webpack or Rollup to support the creation of a UMD module suitable for use in a browser. TypeScript should be configured to output to ES2017 with ES modules and ES5 with CommonJS modules. The bundler would then consume the CommonJS version and output an ES5 UMD module.
Switching would consist of creating a TypeScript config file along with a bundler config file, modifying the scripts in
package.json
to call the TypeScript compiler and bundler, and editing themain
property ofpackage.json
to point to the CommonJS output. We'd also add a newmodule
property pointing to the ES modules output.The only major code changes necessary in this step is removing the Babel polyfill along with the Browserify "hack" at the bottom of
index.js
. The Babel polyfill is no longer needed in the output as TypeScript will correctly polyfill when converting to ES5. The ES modules version should not have any polyfills.After this step is completed, a new version of the package can be published. At this step, the primary benefits would be a more modern build system, he ability for library consumers to employ tree-shaking, and a reduced bundle size for library consumers due to the removal of the Babel polyfill.
2. Incrementally transition to TypeScript
Once the TypeScript compiler and bundler are implemented, work can begin on adding types to the codebase. Ideally, we'd work from the outer edges of the module inward. Thankfully both
kinto.js
andkinto-http.js
only rely on theuuid
module as an external dependency, which already has typings published.For kinto-http.js, this would be the suggested order of transition:
batch.js
errors.js
endpoint.js
utils.js
http.js
requests.js
collection.js
bucket.js
base.js
index.js
For kinto.js, this would be the suggested order of transition:
utils.js
adapters/base.js
adapters/IDB.js
collection.js
KintoBase.js
index.js
Each file can be transitioned individually in its own pull request so that it makes reviewing easier. As we progress upward through the library, it should require less and less typing as TypeScript will be able to automatically infer more and more types.
3. Publish typings
Once all files are written in TypeScript, we can enable declaration file emission in the TypeScript configuration. Once published, any user using TypeScript or a TypeScript-enabled tool will then automatically get type-checking in their codebase.
Build Process Changes
As part of the first transition step, we'd replace Browserify with TypeScript and a bundler like Webpack or Rollup (which one is largely inconsequential as we'd only be using them for bundling ES modules into a UMD bundle; in this sense they're quite similar). We'd also remove Babel from the build process and let TypeScript handle the step down to ES5. (It's likely that we'll still need to retain Babel for testing, but there are replacements if we'd like to completely remove it.)
To support the most popular use cases, we'd publish three versions of the library (contained within one NPM package): an ES modules version written in a modern version of Ecmascript and referenced by the
module
property inpackage.json
, a CommonJS version written in ES5 and referenced by themain
properly inpackage.json
, and a UMD module contained within thedist
folder for use in the browser without a bundler. The primary benefits of this structure are allowing users to ship modern code if they want to (as opposed to forcing ES5), along with supporting tree-shaking.Drawbacks
One of the biggest drawbacks to migrating to TypeScript is increasing the barrier to entry for developers who aren't familiar with TypeScript. However, this is offset by adding stronger type checking to library internals, which actually makes it easier for developers to contribute high-quality code.
Rationale and alternatives
TypeScript isn't the only typed JavaScript alternative. Flow offers a similar approach to TypeScript. However, community support for Flow isn't as large as it is for TypeScript. Flow also lacks large, public, open-source projects that implement it. Recently, Jest switched from Flow to TypeScript.
Prior art
The following are projects that transitioned to TypeScript:
Unresolved questions
Future possibilities
Switching to TypeScript and reworking the build process could be a good opportunity to rethink the testing process. Currently,
kinto.js
andkinto-http.js
have over 30 dependencies that are related to testing. By switching to a new testing tool, particularly one that already understands TypeScript, we might be able to reduce the number of dependencies (and thus the number of pull requests that need review).Currently functions are documented using JSDoc. With the transition to TypeScript, we might be able to also transition to TSDoc.
Frequently Asked Questions
If Kinto is written in TypeScript, would it be possible to use it without TypeScript?
Yes! Even if the project is written in TypeScript, all consumed versions of the library will be written in JavaScript. If you use a TypeScript-aware tool like VS Code, you'll even get type checking for your JavaScript code when interacting with Kinto.
Can we just add typings instead of moving the entire project to TypeScript?
Adding type declarations is a great way to provide some additional safety for library consumers. However, this wouldn't benefit contributors to Kinto, and it would require someone to manually keep the typings up-to-date with Kinto. By writing the project in TypeScript, we can be sure that the typings will always be up-to-date.
What if we decide to switch back to JavaScript?
Reverting to JavaScript is incredibly easy. We can simply replace the TypeScript source files with the TypeScript compiled versions. This will remove the typings, leaving a plain JavaScript file. Also, since we'd start with
kinto-http.js
, we'd be able to detect issues early on before migrating the primary library.The text was updated successfully, but these errors were encountered: